Skip to content
Draft
Show file tree
Hide file tree
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
127 changes: 95 additions & 32 deletions api/src/org/labkey/api/workflow/Action.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public abstract class Action extends CreatedModified
public static final String ASSAY_TYPES_KEY = "assayTypes";
public static final String NUM_PER_PARENT_KEY = "numPerParent";
public static final String UPDATE_STATUS_KEY = "updateStatus";
public static final String REMOVE_FROM_STORAGE_KEY = "removeFromStorage";
public static final String STATUS_KEY = "sampleStatus";
protected Long _rowId;
protected int _ordinal;
Expand Down Expand Up @@ -126,6 +127,42 @@ public void setInputParameters(JSONObject inputParameters)
_inputParameters = inputParameters;
}

private List<String> validateStatus(Container container, String prefix, boolean updateKeyExpected)
{
boolean updateStatus = true;

if (updateKeyExpected)
{
updateStatus = _inputParameters.getBoolean(UPDATE_STATUS_KEY);
if (updateStatus && !_inputParameters.has(STATUS_KEY))
return List.of(prefix + STATUS_KEY + " is required for action of type " + _type + " when " + UPDATE_STATUS_KEY + " is true.");
if (!updateStatus && _inputParameters.has(STATUS_KEY))
return List.of(prefix + STATUS_KEY + " is not allowed for action of type " + _type + " when " + UPDATE_STATUS_KEY + " is false.");
}

if (updateStatus && container != null)
{
try
{
long statusId = _inputParameters.getLong(STATUS_KEY);
SampleStatusService sampleStatusService = SampleStatusService.get();
if (sampleStatusService.getStateForRowId(container, statusId) == null)
return List.of(prefix + "Invalid " + STATUS_KEY + " (" + statusId + ").");
}
catch (Exception e)
{
return List.of(prefix + "Invalid " + STATUS_KEY + ".");
}
}

return Collections.emptyList();
}

private static boolean isSampleStatusKey(String key)
{
return key.equals(STATUS_KEY) || key.equals(UPDATE_STATUS_KEY) || key.equals(REMOVE_FROM_STORAGE_KEY);
}

@JsonIgnore
public List<String> validateInputParameters(int ordinal, Container container)
{
Expand Down Expand Up @@ -169,31 +206,60 @@ public List<String> validateInputParameters(int ordinal, Container container)
}
else if (_type == WorkflowService.ActionType.AliquotSamples)
{
List<String> messages = new ArrayList<>();

if (_inputParameters == null || !_inputParameters.has(NUM_PER_PARENT_KEY))
return List.of(prefix + NUM_PER_PARENT_KEY + " is required for action of type " + _type + ".");
messages.add(prefix + NUM_PER_PARENT_KEY + " is required for action of type " + _type + ".");
else
{
try {
int numPerParent = _inputParameters.getInt(NUM_PER_PARENT_KEY);
if (numPerParent < 0)
return List.of(prefix + NUM_PER_PARENT_KEY + " cannot be negative.");
messages.add(prefix + NUM_PER_PARENT_KEY + " cannot be negative.");
}
catch (Exception e) {
return List.of(prefix + NUM_PER_PARENT_KEY + " must be an integer.");
messages.add(prefix + NUM_PER_PARENT_KEY + " must be an integer.");
}
}

if (_inputParameters != null && (_inputParameters.has(UPDATE_STATUS_KEY) || _inputParameters.has(STATUS_KEY)))
{
List<String> statusMessages = validateStatus(container, prefix, true);
messages.addAll(statusMessages);
}

return messages;
}
else if (_type == WorkflowService.ActionType.DeriveSamples || _type == WorkflowService.ActionType.PoolSamples)
{
if (_inputParameters == null || _inputParameters.isEmpty())
return List.of(prefix + "data about sample types and sample counts per parent is required for action of type " + _type + ".");
if (_type == WorkflowService.ActionType.PoolSamples && _inputParameters.length() > 1)
String emptyMessage = prefix + "data about sample types and sample counts per parent is required for action of type " + _type + ".";

if (_inputParameters == null) return List.of(emptyMessage);

// We can't just check _inputParameters size because it may include sample status keys, so we extract the
// sample type IDs and validate against those.
List<String> sampleTypeIds = new ArrayList<>();
_inputParameters.keys().forEachRemaining(id -> {
if (isSampleStatusKey(id)) return;
sampleTypeIds.add(id);
});

if (sampleTypeIds.isEmpty())
return List.of(emptyMessage);

if (_type == WorkflowService.ActionType.PoolSamples && sampleTypeIds.size() > 1)
return List.of(prefix + "only one sample type can be specified for action of type " + _type + ".");

SampleTypeService sampleTypeService = SampleTypeService.get();
Set<String> invalidSampleTypeIds = new HashSet<>();
List<Object> invalidCounts = new ArrayList<>();
List<String> messages = new ArrayList<>();

for (String id : sampleTypeIds)
{
// validated above
if (isSampleStatusKey(id)) continue;

_inputParameters.keys().forEachRemaining(id -> {
try
{
if (sampleTypeService.getSampleType(Long.valueOf(id)) == null)
Expand All @@ -204,54 +270,51 @@ else if (_type == WorkflowService.ActionType.DeriveSamples || _type == WorkflowS
invalidSampleTypeIds.add(id);
}
Object countObj = _inputParameters.get(id);
if ((countObj instanceof String))
if (countObj instanceof String countStr)
try
{
if (Integer.parseInt((String) countObj) < 0)
if (Integer.parseInt(countStr) < 0)
invalidCounts.add(countObj);
}
catch (NumberFormatException e)
{
invalidCounts.add(countObj);
}
else if (countObj instanceof Integer)
else if (countObj instanceof Integer count)
{
if (((Integer) countObj) < 0)
if (count < 0)
invalidCounts.add(countObj);
}
else
invalidCounts.add(countObj);
});
List<String> messages = new ArrayList<>();
}

if (!invalidSampleTypeIds.isEmpty())
messages.add(prefix + "invalid sample type IDs " + invalidSampleTypeIds + ".");
if (!invalidCounts.isEmpty())
messages.add(prefix + "invalid sample count values " + invalidCounts + ".");

if (_inputParameters.has(UPDATE_STATUS_KEY) || _inputParameters.has(STATUS_KEY))
{
List<String> statusMessages = validateStatus(container, prefix, true);

if (!statusMessages.isEmpty())
messages.addAll(statusMessages);
}

return messages;
}
else if (_type == WorkflowService.ActionType.RemoveFromStorage)
{
if (_inputParameters == null || _inputParameters.isEmpty())
return Collections.emptyList();
boolean updateStatus = _inputParameters.getBoolean(UPDATE_STATUS_KEY);
if (updateStatus && !_inputParameters.has(STATUS_KEY))
return List.of(prefix + STATUS_KEY + " is required for action of type " + _type + " when " + UPDATE_STATUS_KEY + " is true.");
if (!updateStatus && _inputParameters.has(STATUS_KEY))
return List.of(prefix + STATUS_KEY + " is not allowed for action of type " + _type + " when " + UPDATE_STATUS_KEY + " is false.");
if (updateStatus && container != null)
{
try
{
long statusId = _inputParameters.getLong(STATUS_KEY);
SampleStatusService sampleStatusService = SampleStatusService.get();
if (sampleStatusService.getStateForRowId(container, statusId) == null)
return List.of(prefix + "Invalid " + STATUS_KEY + " (" + statusId + ").");
}
catch (Exception e)
{
return List.of(prefix + "Invalid " + STATUS_KEY + ".");
}
}

return validateStatus(container, prefix, true);
} else if (_type == WorkflowService.ActionType.UpdateSampleStatus) {
if (_inputParameters == null || _inputParameters.isEmpty())
return Collections.emptyList();

return validateStatus(container, prefix, false);
} else {
if (_inputParameters != null && !_inputParameters.isEmpty())
return List.of(prefix + "input parameters are not allowed for action of type " + _type + ".");
Expand Down
3 changes: 2 additions & 1 deletion api/src/org/labkey/api/workflow/WorkflowService.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ enum ActionType
MoveInStorage("input parameters", "Moved samples in storage"),
CheckOut("input parameters", "Checked out samples"),
CheckIn("input parameters", "Checked in samples"),
RemoveFromStorage("sample status value", "Removed samples from storage");
RemoveFromStorage("sample status value", "Removed samples from storage"),
UpdateSampleStatus("sample status value", "Updated sample status");

private final String _inputDescription;
private final String _auditMessage;
Expand Down