Skip to content

Commit 90fd8b7

Browse files
Delete resources in the sink DB and FHIR server (#1183)
* Delete resources in the sink DB and FHIR server * Review comments
1 parent 062cc3d commit 90fd8b7

File tree

6 files changed

+83
-15
lines changed

6 files changed

+83
-15
lines changed

pipelines/batch/src/main/java/com/google/fhir/analytics/ConvertResourceFn.java

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,8 @@ public void writeResource(HapiRowDescriptor element)
108108
String jsonResource = element.jsonResource();
109109
long startTime = System.currentTimeMillis();
110110
Resource resource = null;
111-
if (jsonResource == null || jsonResource.isBlank()) {
111+
boolean isResourceDeleted = jsonResource == null || jsonResource.isBlank();
112+
if (isResourceDeleted) {
112113
// The jsonResource field will be empty in case of deleted records and are ignored during
113114
// the initial batch run
114115
if (!processDeletedRecords) {
@@ -149,15 +150,19 @@ public void writeResource(HapiRowDescriptor element)
149150
}
150151
if (!sinkPath.isEmpty()) {
151152
startTime = System.currentTimeMillis();
152-
// TODO : Remove the deleted resources from the sink fhir store
153-
// https://github.com/google/fhir-data-pipes/issues/588
154-
fhirStoreUtil.uploadResource(resource);
153+
if (isResourceDeleted) {
154+
fhirStoreUtil.deleteResourceById(resourceType, resource.getId());
155+
} else {
156+
fhirStoreUtil.uploadResource(resource);
157+
}
155158
totalPushTimeMillisMap.get(resourceType).inc(System.currentTimeMillis() - startTime);
156159
}
157160
if (sinkDbConfig != null) {
158-
// TODO : Remove the deleted resources from the sink database
159-
// https://github.com/google/fhir-data-pipes/issues/588
160-
jdbcWriter.writeResource(resource);
161+
if (isResourceDeleted) {
162+
jdbcWriter.deleteResourceById(resourceType, resource.getId());
163+
} else {
164+
jdbcWriter.writeResource(resource);
165+
}
161166
}
162167
}
163168

pipelines/batch/src/main/java/com/google/fhir/analytics/JdbcFetchHapi.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,7 @@ public FetchRowsJdbcIo(
161161
+ " FROM hfj_resource res JOIN"
162162
+ " hfj_res_ver ver ON res.res_id = ver.res_id AND res.res_ver = ver.res_ver "
163163
+ " LEFT JOIN hfj_forced_id hfi ON res.res_id = hfi.resource_pid "
164-
+ " WHERE res.res_type = ? AND res.res_id % ? = ? AND"
165-
+ " ver.res_encoding != 'DEL'");
164+
+ " WHERE res.res_type = ? AND res.res_id % ? = ?");
166165
// TODO do date sanity-checking on `since` (note this is partly done by HAPI client call).
167166
if (since != null && !since.isEmpty()) {
168167
builder.append(" AND res.res_updated > '").append(since).append("'");

pipelines/batch/src/main/java/com/google/fhir/analytics/JdbcResourceWriter.java

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,7 @@ static void createTables(FhirEtlOptions options)
181181
}
182182
}
183183

184-
// TODO expose this such that we can properly handle deleted FHIR resources in the pipeline.
185-
private static void deleteOldViewRows(DataSource dataSource, String tableName, String resId)
184+
private static void deleteRowsById(DataSource dataSource, String tableName, String resId)
186185
throws SQLException {
187186
String sql = String.format("DELETE FROM %s WHERE %s=? ;", tableName, ID_COLUMN);
188187
try (Connection connection = dataSource.getConnection();
@@ -192,6 +191,27 @@ private static void deleteOldViewRows(DataSource dataSource, String tableName, S
192191
}
193192
}
194193

194+
/**
195+
* Deletes a resource based on resourceType and id
196+
*
197+
* @param resourceType the type of resource to be deleted
198+
* @param id the id of the resource to be deleted
199+
* @throws SQLException
200+
*/
201+
public void deleteResourceById(String resourceType, String id) throws SQLException {
202+
if (viewManager == null) {
203+
deleteRowsById(jdbcDataSource, resourceType, id);
204+
} else {
205+
ImmutableList<ViewDefinition> views = viewManager.getViewsForType(resourceType);
206+
for (ViewDefinition vDef : views) {
207+
if (Strings.isNullOrEmpty(vDef.getName())) {
208+
throw new SQLException("Field `name` in ViewDefinition is not defined.");
209+
}
210+
deleteRowsById(jdbcDataSource, vDef.getName(), id);
211+
}
212+
}
213+
}
214+
195215
public void writeResource(Resource resource) throws SQLException, ViewApplicationException {
196216
// TODO merge deletions and insertions into atomic transactions.
197217
if (viewManager == null) {
@@ -220,13 +240,12 @@ public void writeResource(Resource resource) throws SQLException, ViewApplicatio
220240
ViewApplicator applicator = new ViewApplicator(vDef);
221241
RowList rowList = applicator.apply(resource);
222242
// We should first delete old rows produced from the same resource in a previous run:
223-
deleteOldViewRows(
243+
deleteRowsById(
224244
jdbcDataSource, vDef.getName(), ViewApplicator.getIdString(resource.getIdElement()));
225245
StringBuilder builder = new StringBuilder("INSERT INTO ");
226246
builder.append(vDef.getName()).append(" (");
227247
builder.append(String.join(",", rowList.getColumnInfos().keySet()));
228248
builder.append(") VALUES(");
229-
// TODO handle deleted resources: https://github.com/google/fhir-data-pipes/issues/588
230249
builder.append(
231250
String.join(
232251
",",

pipelines/batch/src/test/java/com/google/fhir/analytics/JdbcResourceWriterTest.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,15 @@ public void testWriteResource() throws SQLException, ViewApplicationException {
8686
assertThat(indexCaptor.getAllValues().get(2), equalTo(3));
8787
assertThat(idCaptor.getAllValues().get(2), equalTo("my-patient-id"));
8888
}
89+
90+
@Test
91+
public void testDeleteResource() throws SQLException {
92+
JdbcResourceWriter jdbcResourceWriter = new JdbcResourceWriter(dataSourceMock, "", fhirContext);
93+
String resourceType = "Patient";
94+
String id = "123456";
95+
jdbcResourceWriter.deleteResourceById(resourceType, id);
96+
verify(statementMock, times(1)).setString(indexCaptor.capture(), idCaptor.capture());
97+
assertThat(indexCaptor.getAllValues().get(0), equalTo(1));
98+
assertThat(idCaptor.getAllValues().get(0), equalTo("123456"));
99+
}
89100
}

pipelines/common/src/main/java/com/google/fhir/analytics/FhirStoreUtil.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2023 Google LLC
2+
* Copyright 2020-2024 Google LLC
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -92,6 +92,25 @@ public MethodOutcome uploadResource(Resource resource) {
9292
return updateFhirResource(sinkUrl, resource, interceptors);
9393
}
9494

95+
/**
96+
* Deletes a resource using the given resourceType and id
97+
*
98+
* @param resourceType the type of resource to be deleted
99+
* @param id the id of the resource to be deleted
100+
* @return the output result of delete operation
101+
*/
102+
public MethodOutcome deleteResourceById(String resourceType, String id) {
103+
Collection<IClientInterceptor> interceptors = Collections.emptyList();
104+
if (!isNullOrEmpty(sinkUsername) && !isNullOrEmpty(sinkPassword)) {
105+
interceptors = Collections.singleton(new BasicAuthInterceptor(sinkUsername, sinkPassword));
106+
}
107+
IGenericClient client = createGenericClient(sinkUrl, interceptors);
108+
// Initialize the client, which will be used to interact with the service.
109+
MethodOutcome outcome = client.delete().resourceById(resourceType, id).execute();
110+
log.debug("FHIR resource deleted at" + sinkUrl + "? " + outcome.getCreated());
111+
return outcome;
112+
}
113+
95114
public Collection<MethodOutcome> uploadBundle(Bundle bundle) {
96115
Collection<IClientInterceptor> interceptors = Collections.emptyList();
97116

pipelines/common/src/test/java/com/google/fhir/analytics/FhirStoreUtilTest.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2023 Google LLC
2+
* Copyright 2020-2024 Google LLC
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@
2424
import ca.uhn.fhir.rest.api.MethodOutcome;
2525
import ca.uhn.fhir.rest.client.api.IGenericClient;
2626
import ca.uhn.fhir.rest.client.api.IRestfulClientFactory;
27+
import ca.uhn.fhir.rest.gclient.IDeleteTyped;
2728
import ca.uhn.fhir.rest.gclient.IUpdateTyped;
2829
import java.io.IOException;
2930
import java.io.InputStream;
@@ -50,6 +51,8 @@ public class FhirStoreUtilTest {
5051

5152
@Mock IUpdateTyped iexec;
5253

54+
@Mock IDeleteTyped iDeleteTyped;
55+
5356
private FhirStoreUtil fhirStoreUtil;
5457

5558
private Patient patient;
@@ -119,4 +122,16 @@ public void testUploadBundle() {
119122
assertThat(result, not(Matchers.empty()));
120123
assertThat(result.iterator().next().getCreated(), equalTo(true));
121124
}
125+
126+
@Test
127+
public void testDeleteResource() {
128+
String resourceType = "Patient";
129+
String id = "patient-id";
130+
when(client.delete().resourceById(resourceType, id)).thenReturn(iDeleteTyped);
131+
MethodOutcome outcome = new MethodOutcome();
132+
outcome.setCreated(true);
133+
doReturn(outcome).when(iDeleteTyped).execute();
134+
MethodOutcome result = fhirStoreUtil.deleteResourceById(resourceType, id);
135+
assertThat(result.getCreated(), equalTo(true));
136+
}
122137
}

0 commit comments

Comments
 (0)