|
15 | 15 | */ |
16 | 16 | package org.labkey.study.assay; |
17 | 17 |
|
| 18 | +import org.labkey.api.data.CompareType; |
18 | 19 | import org.labkey.api.data.Container; |
19 | 20 | import org.labkey.api.data.SimpleFilter; |
20 | 21 | import org.labkey.api.data.TableInfo; |
|
30 | 31 | import org.labkey.api.query.QueryService; |
31 | 32 | import org.labkey.api.query.UserSchema; |
32 | 33 | import org.labkey.api.query.ValidationException; |
| 34 | +import org.labkey.api.security.ElevatedUser; |
33 | 35 | import org.labkey.api.security.User; |
34 | 36 | import org.labkey.api.security.permissions.DeletePermission; |
| 37 | +import org.labkey.api.security.roles.ReaderRole; |
35 | 38 | import org.labkey.api.study.Dataset; |
36 | 39 | import org.labkey.api.study.publish.StudyPublishService; |
37 | 40 | import org.labkey.api.view.UnauthorizedException; |
|
41 | 44 | import java.util.HashMap; |
42 | 45 | import java.util.List; |
43 | 46 | import java.util.Map; |
| 47 | +import java.util.Set; |
44 | 48 |
|
45 | 49 | import static java.util.Collections.singleton; |
46 | 50 |
|
@@ -72,41 +76,58 @@ public void beforeMaterialDelete(List<? extends ExpMaterial> materials, Containe |
72 | 76 |
|
73 | 77 | // It's likely that we'll have multiple materials from the same sample type, so group them for efficient processing |
74 | 78 |
|
75 | | - Map<ExpSampleType, List<ExpMaterial>> typeToMaterials = new HashMap<>(); |
| 79 | + Map<ExpSampleType, Map<Container, List<ExpMaterial>>> typeToMaterials = new HashMap<>(); |
76 | 80 |
|
77 | 81 | for (ExpMaterial material: materials) |
78 | 82 | { |
79 | 83 | ExpSampleType sampleType = material.getSampleType(); |
80 | 84 | if (sampleType != null) |
81 | 85 | { |
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); |
83 | 91 | } |
84 | 92 | } |
85 | 93 |
|
86 | | - for (Map.Entry<ExpSampleType, List<ExpMaterial>> entry : typeToMaterials.entrySet()) |
| 94 | + for (Map.Entry<ExpSampleType, Map<Container, List<ExpMaterial>>> entry : typeToMaterials.entrySet()) |
87 | 95 | { |
88 | 96 | for (Dataset dataset: StudyPublishService.get().getDatasetsForPublishSource(entry.getKey().getRowId(), Dataset.PublishSource.SampleType)) |
89 | 97 | { |
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); |
95 | 102 |
|
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); |
98 | 107 |
|
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()) |
101 | 109 | { |
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(); |
104 | 117 |
|
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)) |
106 | 125 | { |
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); |
109 | 127 | } |
| 128 | + |
| 129 | + StudyPublishService.get().addRecallAuditEvent(sampleContainer, user, dataset, linkedLsids.size(), linkedRowIds); |
| 130 | + dataset.deleteDatasetRows(user, linkedLsids); |
110 | 131 | } |
111 | 132 | } |
112 | 133 | } |
|
0 commit comments