Skip to content

Commit d498369

Browse files
committed
GH Issue 1214:GetRunQCHistoryAction to get audit events for QC state changes
- need to use elevated user with CanSeeAuditLog permissions to get events for the run
1 parent e8b4f8d commit d498369

1 file changed

Lines changed: 54 additions & 0 deletions

File tree

assay/src/org/labkey/assay/AssayController.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.labkey.api.action.AbstractFileUploadAction;
2828
import org.labkey.api.action.ApiResponse;
2929
import org.labkey.api.action.ApiSimpleResponse;
30+
import org.labkey.api.action.ExtendedApiQueryResponse;
3031
import org.labkey.api.action.Marshal;
3132
import org.labkey.api.action.Marshaller;
3233
import org.labkey.api.action.MutatingApiAction;
@@ -77,6 +78,7 @@
7778
import org.labkey.api.data.JsonWriter;
7879
import org.labkey.api.data.MutableColumnInfo;
7980
import org.labkey.api.data.SimpleFilter;
81+
import org.labkey.api.data.Sort;
8082
import org.labkey.api.data.TableInfo;
8183
import org.labkey.api.data.TableSelector;
8284
import org.labkey.api.defaults.DefaultValueService;
@@ -1482,6 +1484,58 @@ public void addNavTrail(NavTree root)
14821484
}
14831485
}
14841486

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+
14851539
@RequiresPermission(QCAnalystPermission.class)
14861540
public static class UpdateQCStateAction extends MutatingApiAction<UpdateQCStateForm>
14871541
{

0 commit comments

Comments
 (0)