Skip to content

Commit

Permalink
Merge pull request #18517 from mvdbeek/fix_deleted_ipnut_submission_c…
Browse files Browse the repository at this point in the history
…ollection

[24.1] Prevent job submission if input collection element is deleted
  • Loading branch information
mvdbeek authored Jul 10, 2024
2 parents af6e28d + 0be837e commit ba3c58d
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 13 deletions.
17 changes: 17 additions & 0 deletions lib/galaxy/model/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6499,6 +6499,23 @@ def attribute_columns(column_collection, attributes, nesting_level=None):
q = q.order_by(*order_by_columns)
return q

@property
def elements_deleted(self):
if not hasattr(self, "_elements_deleted"):
if session := object_session(self):
stmt = self._build_nested_collection_attributes_stmt(
hda_attributes=("deleted",), dataset_attributes=("deleted",)
)
stmt = stmt.exists().where(or_(HistoryDatasetAssociation.deleted == true(), Dataset.deleted == true()))
self._elements_deleted = session.execute(select(stmt)).scalar()
else:
self._elements_deleted = False
for dataset_instance in self.dataset_instances:
if dataset_instance.deleted or dataset_instance.dataset.deleted:
self._elements_deleted = True
break
return self._elements_deleted

@property
def dataset_states_and_extensions_summary(self):
if not hasattr(self, "_dataset_states_and_extensions_summary"):
Expand Down
53 changes: 40 additions & 13 deletions lib/galaxy/tools/parameters/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -2189,19 +2189,37 @@ def from_json(self, value, trans, other_values=None):
dataset_matcher_factory = get_dataset_matcher_factory(trans)
dataset_matcher = dataset_matcher_factory.dataset_matcher(self, other_values)
for v in rval:
if v:
if hasattr(v, "deleted") and v.deleted:
if isinstance(v, DatasetCollectionElement):
if hda := v.hda:
v = hda
elif ldda := v.ldda:
v = ldda
elif collection := v.child_collection:
v = collection
elif not v.collection and v.collection.populated_optimized:
raise ParameterValueError("the selected collection has not been populated.", self.name)
else:
raise ParameterValueError("Collection element in unexpected state", self.name)
if isinstance(v, DatasetInstance):
if v.deleted:
raise ParameterValueError("the previously selected dataset has been deleted.", self.name)
elif hasattr(v, "dataset") and v.dataset.state in [Dataset.states.ERROR, Dataset.states.DISCARDED]:
elif v.dataset and v.dataset.state in [Dataset.states.ERROR, Dataset.states.DISCARDED]:
raise ParameterValueError(
"the previously selected dataset has entered an unusable state", self.name
)
elif hasattr(v, "dataset"):
if isinstance(v, DatasetCollectionElement):
v = v.hda
match = dataset_matcher.hda_match(v)
if match and match.implicit_conversion:
v.implicit_conversion = True # type:ignore[union-attr]
match = dataset_matcher.hda_match(v)
if match and match.implicit_conversion:
v.implicit_conversion = True # type:ignore[union-attr]
elif isinstance(v, HistoryDatasetCollectionAssociation):
if v.deleted:
raise ParameterValueError("the previously selected dataset collection has been deleted.", self.name)
v = v.collection
if isinstance(v, DatasetCollection):
if v.elements_deleted:
raise ParameterValueError(
"the previously selected dataset collection has elements that are deleted.", self.name
)

if not self.multiple:
if len(rval) > 1:
raise ParameterValueError("more than one dataset supplied to single input dataset parameter", self.name)
Expand Down Expand Up @@ -2498,10 +2516,19 @@ def from_json(self, value, trans, other_values=None):
rval = session.get(HistoryDatasetCollectionAssociation, int(value[len("hdca:") :]))
else:
rval = session.get(HistoryDatasetCollectionAssociation, int(value))
if rval and isinstance(rval, HistoryDatasetCollectionAssociation):
if rval.deleted:
raise ParameterValueError("the previously selected dataset collection has been deleted", self.name)
# TODO: Handle error states, implement error states ...
if rval:
if isinstance(rval, HistoryDatasetCollectionAssociation):
if rval.deleted:
raise ParameterValueError("the previously selected dataset collection has been deleted", self.name)
if rval.collection.elements_deleted:
raise ParameterValueError(
"the previously selected dataset collection has elements that are deleted.", self.name
)
if isinstance(rval, DatasetCollectionElement):
if (child_collection := rval.child_collection) and child_collection.elements_deleted:
raise ParameterValueError(
"the previously selected dataset collection has elements that are deleted.", self.name
)
return rval

def to_text(self, value):
Expand Down
17 changes: 17 additions & 0 deletions lib/galaxy_test/api/test_jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,23 @@ def paths_deleted():
if output_dataset_paths_exist:
wait_on(paths_deleted, "path deletion")

def test_submission_on_collection_with_deleted_element(self, history_id):
hdca = self.dataset_collection_populator.create_list_of_list_in_history(history_id=history_id, wait=True).json()
hda_id = hdca["elements"][0]["object"]["elements"][0]["object"]["id"]
self.dataset_populator.delete_dataset(history_id=history_id, content_id=hda_id)
response = self.dataset_populator.run_tool_raw(
"is_of_type",
inputs={
"collection": {"batch": True, "values": [{"src": "hdca", "id": hdca["id"], "map_over_type": "list"}]},
},
history_id=history_id,
)
assert response.status_code == 400
assert (
response.json()["err_msg"]
== "parameter 'collection': the previously selected dataset collection has elements that are deleted."
)

@pytest.mark.require_new_history
@skip_without_tool("create_2")
def test_purging_output_cleaned_after_ok_run(self, history_id):
Expand Down

0 comments on commit ba3c58d

Please sign in to comment.