Skip to content

Commit e730d71

Browse files
authored
GitHub Issue 1028: Can't delete a sample when any sample in the sample type has been linked to study (#7572)
1 parent 7cc7129 commit e730d71

4 files changed

Lines changed: 45 additions & 23 deletions

File tree

api/src/org/labkey/api/study/publish/StudyPublishService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ ActionURL publishData(User user, Container sourceContainer, @Nullable Container
156156

157157
String checkForLockedLinks(Dataset def, @Nullable List<Long> rowIds);
158158

159-
void addRecallAuditEvent(Container sourceContainer, User user, Dataset def, int rowCount, @Nullable Collection<Pair<String,Long>> datasetRowLsidAndSourceRowIds);
159+
void addRecallAuditEvent(Container sourceContainer, User user, Dataset def, int rowCount, @Nullable Collection<Long> rowIds);
160160

161161
/**
162162
* Adds columns to an assay data table, providing a link to any datasets that have

study/src/org/labkey/study/assay/ExperimentListenerImpl.java

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.labkey.study.assay;
1717

18+
import org.labkey.api.data.CompareType;
1819
import org.labkey.api.data.Container;
1920
import org.labkey.api.data.SimpleFilter;
2021
import org.labkey.api.data.TableInfo;
@@ -30,8 +31,10 @@
3031
import org.labkey.api.query.QueryService;
3132
import org.labkey.api.query.UserSchema;
3233
import org.labkey.api.query.ValidationException;
34+
import org.labkey.api.security.ElevatedUser;
3335
import org.labkey.api.security.User;
3436
import org.labkey.api.security.permissions.DeletePermission;
37+
import org.labkey.api.security.roles.ReaderRole;
3538
import org.labkey.api.study.Dataset;
3639
import org.labkey.api.study.publish.StudyPublishService;
3740
import org.labkey.api.view.UnauthorizedException;
@@ -41,6 +44,7 @@
4144
import java.util.HashMap;
4245
import java.util.List;
4346
import java.util.Map;
47+
import java.util.Set;
4448

4549
import static java.util.Collections.singleton;
4650

@@ -72,41 +76,58 @@ public void beforeMaterialDelete(List<? extends ExpMaterial> materials, Containe
7276

7377
// It's likely that we'll have multiple materials from the same sample type, so group them for efficient processing
7478

75-
Map<ExpSampleType, List<ExpMaterial>> typeToMaterials = new HashMap<>();
79+
Map<ExpSampleType, Map<Container, List<ExpMaterial>>> typeToMaterials = new HashMap<>();
7680

7781
for (ExpMaterial material: materials)
7882
{
7983
ExpSampleType sampleType = material.getSampleType();
8084
if (sampleType != null)
8185
{
82-
typeToMaterials.computeIfAbsent(sampleType, x -> new ArrayList<>()).add(material);
86+
Container materialContainer = material.getContainer();
87+
typeToMaterials.
88+
computeIfAbsent(sampleType, k -> new HashMap<>()).
89+
computeIfAbsent(materialContainer, k -> new ArrayList<>()).
90+
add(material);
8391
}
8492
}
8593

86-
for (Map.Entry<ExpSampleType, List<ExpMaterial>> entry : typeToMaterials.entrySet())
94+
for (Map.Entry<ExpSampleType, Map<Container, List<ExpMaterial>>> entry : typeToMaterials.entrySet())
8795
{
8896
for (Dataset dataset: StudyPublishService.get().getDatasetsForPublishSource(entry.getKey().getRowId(), Dataset.PublishSource.SampleType))
8997
{
90-
TableInfo t = dataset.getTableInfo(user);
91-
if (null == t || !t.hasPermission(user, DeletePermission.class))
92-
{
93-
throw new UnauthorizedException("Cannot delete rows from dataset " + dataset);
94-
}
98+
Map<Container, List<ExpMaterial>> containerSamples = entry.getValue();
99+
100+
// Need Read permission to check for linked samples
101+
User userWithReadPerm = ElevatedUser.getElevatedUser(user, ReaderRole.class);
95102

96-
UserSchema schema = QueryService.get().getUserSchema(user, dataset.getContainer(), "study");
97-
TableInfo tableInfo = schema.getTable(dataset.getName());
103+
UserSchema schemaWithReadPerm = QueryService.get().getUserSchema(userWithReadPerm, dataset.getContainer(), "study");
104+
TableInfo tableInfoForRead = schemaWithReadPerm.getTable(dataset.getName());
105+
if (null == tableInfoForRead)
106+
throw new UnauthorizedException("Cannot delete rows from dataset " + dataset);
98107

99-
// Future optimization - query for all the materials at once
100-
for (ExpMaterial material : entry.getValue())
108+
for (Map.Entry<Container, List<ExpMaterial>> containerEntry : containerSamples.entrySet())
101109
{
102-
SimpleFilter filter = new SimpleFilter(FieldKey.fromParts(ExpMaterialTable.Column.RowId.toString()), material.getRowId());
103-
String lsid = new TableSelector(tableInfo, singleton("LSID"), filter, null).getObject(String.class);
110+
Container sampleContainer = containerEntry.getKey();
111+
List<ExpMaterial> samples = containerEntry.getValue();
112+
113+
// GitHub Issue 1028: Can't delete a sample when any sample in the sample type has been linked to study
114+
// check if samples are linked to the dataset, if not, skip the permission check for DeletePermission since we won't be deleting any rows
115+
SimpleFilter filter = new SimpleFilter(FieldKey.fromParts(ExpMaterialTable.Column.RowId.toString()), samples.stream().map(ExpMaterial::getRowId).toList(), CompareType.IN);
116+
Map<String, Object>[] linkedLsidRowIds = new TableSelector(tableInfoForRead, Set.of("LSID", "RowId"), filter, null).getMapArray();
104117

105-
if (lsid != null)
118+
Set<String> linkedLsids = Arrays.stream(linkedLsidRowIds).map(m -> (String)m.get("LSID")).collect(java.util.stream.Collectors.toSet());
119+
Set<Long> linkedRowIds = Arrays.stream(linkedLsidRowIds).map(m -> ((Integer)m.get("RowId")).longValue()).collect(java.util.stream.Collectors.toSet());
120+
if (linkedLsids.isEmpty())
121+
continue;
122+
123+
TableInfo tableInfo = dataset.getTableInfo(user);
124+
if (null == tableInfo || !tableInfo.hasPermission(user, DeletePermission.class))
106125
{
107-
StudyPublishService.get().addRecallAuditEvent(material.getContainer(), user, dataset, 1, null);
108-
dataset.deleteDatasetRows(user, Arrays.asList(lsid));
126+
throw new UnauthorizedException("Cannot delete rows from dataset " + dataset);
109127
}
128+
129+
StudyPublishService.get().addRecallAuditEvent(sampleContainer, user, dataset, linkedLsids.size(), linkedRowIds);
130+
dataset.deleteDatasetRows(user, linkedLsids);
110131
}
111132
}
112133
}

study/src/org/labkey/study/assay/StudyPublishManager.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1518,7 +1518,7 @@ public String checkForLockedLinks(Dataset def, @Nullable List<Long> rowIds)
15181518
}
15191519

15201520
@Override
1521-
public void addRecallAuditEvent(Container sourceContainer, User user, Dataset def, int rowCount, @Nullable Collection<Pair<String,Long>> pairs)
1521+
public void addRecallAuditEvent(Container sourceContainer, User user, Dataset def, int rowCount, @Nullable Collection<Long> rowIds)
15221522
{
15231523
Dataset.PublishSource sourceType = def.getPublishSource();
15241524
if (sourceType != null)
@@ -1539,15 +1539,14 @@ public void addRecallAuditEvent(Container sourceContainer, User user, Dataset de
15391539
AuditLogService.get().addEvent(user, event);
15401540

15411541
// Create sample timeline event for each of the samples
1542-
if (sourceType == Dataset.PublishSource.SampleType && pairs != null)
1542+
if (sourceType == Dataset.PublishSource.SampleType && rowIds != null && !rowIds.isEmpty())
15431543
{
15441544
var timelineEventType = SampleTimelineAuditEvent.SampleTimelineEventType.RECALL;
15451545
Map<String, Object> eventMetadata = new HashMap<>();
15461546
eventMetadata.put(SAMPLE_TIMELINE_EVENT_TYPE, timelineEventType.name());
15471547
String metadata = AbstractAuditTypeProvider.encodeForDataMap(eventMetadata);
15481548

1549-
List<Long> sampleIds = pairs.stream().map(Pair::getValue).collect(toList());
1550-
List<? extends ExpMaterial> samples = ExperimentService.get().getExpMaterials(sampleIds);
1549+
List<? extends ExpMaterial> samples = ExperimentService.get().getExpMaterials(rowIds);
15511550
List<AuditTypeEvent> events = new ArrayList<>(samples.size());
15521551
for (ExpMaterial sample : samples)
15531552
{

study/src/org/labkey/study/controllers/StudyController.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@
313313
import java.util.concurrent.TimeUnit;
314314
import java.util.function.Predicate;
315315
import java.util.regex.Pattern;
316+
import java.util.stream.Collectors;
316317

317318
import static org.labkey.api.util.IntegerUtils.asInteger;
318319
import static org.labkey.study.model.QCStateSet.PUBLIC_STATES_LABEL;
@@ -3106,9 +3107,10 @@ public boolean handlePost(DeleteDatasetRowsForm form, BindException errors)
31063107
{
31073108
String sourceLsid = entry.getKey();
31083109
Collection<Pair<String, Long>> pairs = entry.getValue();
3110+
Collection<Long> rowIds = pairs.stream().map(Pair::getValue).collect(Collectors.toList());
31093111
Container sourceContainer = publishSource.resolveSourceLsidContainer(sourceLsid, _sourceRowId);
31103112
if (sourceContainer != null)
3111-
StudyPublishService.get().addRecallAuditEvent(sourceContainer, getUser(), _def, pairs.size(), pairs);
3113+
StudyPublishService.get().addRecallAuditEvent(sourceContainer, getUser(), _def, pairs.size(), rowIds);
31123114
}
31133115
}
31143116

0 commit comments

Comments
 (0)