Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions assay/src/org/labkey/assay/AssayController.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.labkey.api.action.AbstractFileUploadAction;
import org.labkey.api.action.ApiResponse;
import org.labkey.api.action.ApiSimpleResponse;
import org.labkey.api.action.ExtendedApiQueryResponse;
import org.labkey.api.action.Marshal;
import org.labkey.api.action.Marshaller;
import org.labkey.api.action.MutatingApiAction;
Expand Down Expand Up @@ -77,6 +78,7 @@
import org.labkey.api.data.JsonWriter;
import org.labkey.api.data.MutableColumnInfo;
import org.labkey.api.data.SimpleFilter;
import org.labkey.api.data.Sort;
import org.labkey.api.data.TableInfo;
import org.labkey.api.data.TableSelector;
import org.labkey.api.defaults.DefaultValueService;
Expand Down Expand Up @@ -1482,6 +1484,58 @@ public void addNavTrail(NavTree root)
}
}

// GH Issue 1214: Returns the QC state history (assay/experiment audit events with a QC state) for a single run,
// for consumption by the assay app's run details page. A QC Analyst can edit an assay run's QC state but is not
// otherwise granted audit-log read access, so this action elevates to CanSeeAuditLog just for the history query
// (mirroring the server-rendered QCStateAction above) after confirming the caller can read the run's container.
@RequiresPermission(AssayReadPermission.class)
public static class GetRunQCHistoryAction extends ReadOnlyApiAction<RunQCHistoryForm>
{
@Override
public ApiResponse execute(RunQCHistoryForm form, BindException errors)
{
if (form.getRunId() == null)
throw new NotFoundException("A runId is required.");

// Resolve the run and confirm the (non-elevated) user can read its container BEFORE elevating, so this
// action can't be used to read audit events from a container the user otherwise can't access.
ExpRun run = ExperimentService.get().getExpRun(form.getRunId());
if (run == null || !run.getContainer().hasPermission(getUser(), ReadPermission.class))
throw new NotFoundException("Run " + form.getRunId() + " not found.");

Container runContainer = run.getContainer();
User auditUser = ElevatedUser.ensureCanSeeAuditLogRole(runContainer, getUser());
UserSchema schema = AuditLogService.getAuditLogSchema(auditUser, runContainer);
if (schema == null)
throw new NotFoundException("Audit log schema is not available.");

QuerySettings settings = new QuerySettings(getViewContext(), "qcHistory");
settings.setQueryName("ExperimentAuditEvent");
SimpleFilter filter = new SimpleFilter(FieldKey.fromParts("RunLsid"), run.getLSID());
filter.addCondition(FieldKey.fromParts("QCState"), null, CompareType.NONBLANK);
settings.setBaseFilter(filter);
settings.setBaseSort(new Sort("-Created"));

QueryView view = schema.createView(getViewContext(), settings, errors);
return new ExtendedApiQueryResponse(view, false, false, "auditLog", "ExperimentAuditEvent", 0, null, false, false, false);
}
}

public static class RunQCHistoryForm
{
private Long _runId;

public Long getRunId()
{
return _runId;
}

public void setRunId(Long runId)
{
_runId = runId;
}
}

@RequiresPermission(QCAnalystPermission.class)
public static class UpdateQCStateAction extends MutatingApiAction<UpdateQCStateForm>
{
Expand Down