|
27 | 27 | import org.labkey.api.action.AbstractFileUploadAction; |
28 | 28 | import org.labkey.api.action.ApiResponse; |
29 | 29 | import org.labkey.api.action.ApiSimpleResponse; |
| 30 | +import org.labkey.api.action.ExtendedApiQueryResponse; |
30 | 31 | import org.labkey.api.action.Marshal; |
31 | 32 | import org.labkey.api.action.Marshaller; |
32 | 33 | import org.labkey.api.action.MutatingApiAction; |
|
77 | 78 | import org.labkey.api.data.JsonWriter; |
78 | 79 | import org.labkey.api.data.MutableColumnInfo; |
79 | 80 | import org.labkey.api.data.SimpleFilter; |
| 81 | +import org.labkey.api.data.Sort; |
80 | 82 | import org.labkey.api.data.TableInfo; |
81 | 83 | import org.labkey.api.data.TableSelector; |
82 | 84 | import org.labkey.api.defaults.DefaultValueService; |
@@ -1482,6 +1484,58 @@ public void addNavTrail(NavTree root) |
1482 | 1484 | } |
1483 | 1485 | } |
1484 | 1486 |
|
| 1487 | + // GH Issue 1214: Returns the QC state history (assay/experiment audit events with a QC state) for a single run, |
| 1488 | + // for consumption by the assay app's run details page. A QC Analyst can edit an assay run's QC state but is not |
| 1489 | + // otherwise granted audit-log read access, so this action elevates to CanSeeAuditLog just for the history query |
| 1490 | + // (mirroring the server-rendered QCStateAction above) after confirming the caller can read the run's container. |
| 1491 | + @RequiresPermission(AssayReadPermission.class) |
| 1492 | + public static class GetRunQCHistoryAction extends ReadOnlyApiAction<RunQCHistoryForm> |
| 1493 | + { |
| 1494 | + @Override |
| 1495 | + public ApiResponse execute(RunQCHistoryForm form, BindException errors) |
| 1496 | + { |
| 1497 | + if (form.getRunId() == null) |
| 1498 | + throw new NotFoundException("A runId is required."); |
| 1499 | + |
| 1500 | + // Resolve the run and confirm the (non-elevated) user can read its container BEFORE elevating, so this |
| 1501 | + // action can't be used to read audit events from a container the user otherwise can't access. |
| 1502 | + ExpRun run = ExperimentService.get().getExpRun(form.getRunId()); |
| 1503 | + if (run == null || !run.getContainer().hasPermission(getUser(), ReadPermission.class)) |
| 1504 | + throw new NotFoundException("Run " + form.getRunId() + " not found."); |
| 1505 | + |
| 1506 | + Container runContainer = run.getContainer(); |
| 1507 | + User auditUser = ElevatedUser.ensureCanSeeAuditLogRole(runContainer, getUser()); |
| 1508 | + UserSchema schema = AuditLogService.getAuditLogSchema(auditUser, runContainer); |
| 1509 | + if (schema == null) |
| 1510 | + throw new NotFoundException("Audit log schema is not available."); |
| 1511 | + |
| 1512 | + QuerySettings settings = new QuerySettings(getViewContext(), "qcHistory"); |
| 1513 | + settings.setQueryName("ExperimentAuditEvent"); |
| 1514 | + SimpleFilter filter = new SimpleFilter(FieldKey.fromParts("RunLsid"), run.getLSID()); |
| 1515 | + filter.addCondition(FieldKey.fromParts("QCState"), null, CompareType.NONBLANK); |
| 1516 | + settings.setBaseFilter(filter); |
| 1517 | + settings.setBaseSort(new Sort("-Created")); |
| 1518 | + |
| 1519 | + QueryView view = schema.createView(getViewContext(), settings, errors); |
| 1520 | + return new ExtendedApiQueryResponse(view, false, false, "auditLog", "ExperimentAuditEvent", 0, null, false, false, false); |
| 1521 | + } |
| 1522 | + } |
| 1523 | + |
| 1524 | + public static class RunQCHistoryForm |
| 1525 | + { |
| 1526 | + private Long _runId; |
| 1527 | + |
| 1528 | + public Long getRunId() |
| 1529 | + { |
| 1530 | + return _runId; |
| 1531 | + } |
| 1532 | + |
| 1533 | + public void setRunId(Long runId) |
| 1534 | + { |
| 1535 | + _runId = runId; |
| 1536 | + } |
| 1537 | + } |
| 1538 | + |
1485 | 1539 | @RequiresPermission(QCAnalystPermission.class) |
1486 | 1540 | public static class UpdateQCStateAction extends MutatingApiAction<UpdateQCStateForm> |
1487 | 1541 | { |
|
0 commit comments