From f79692760ee33ebb4f6dfb8db7d8eba4bd50f38b Mon Sep 17 00:00:00 2001 From: mvdbeek Date: Thu, 19 Mar 2026 14:30:59 +0100 Subject: [PATCH] Return async task result --- client/src/api/schema/schema.ts | 30 ++++++++++++++++ lib/galaxy/celery/tasks.py | 2 +- lib/galaxy/managers/histories.py | 4 ++- lib/galaxy/schema/schema.py | 12 +++++++ .../webapps/galaxy/services/histories.py | 8 +++-- test/integration/test_purge_datasets.py | 36 +++++++++---------- 6 files changed, 68 insertions(+), 24 deletions(-) diff --git a/client/src/api/schema/schema.ts b/client/src/api/schema/schema.ts index 710506133cb..de8de9ba3bc 100644 --- a/client/src/api/schema/schema.ts +++ b/client/src/api/schema/schema.ts @@ -7463,6 +7463,11 @@ export interface components { * @description Whether this resource is currently publicly available to all users. */ published: boolean; + /** + * Purge Task + * @description Summary of the async task purging datasets in this history. Only present when purge is performed via a background task. + */ + purge_task?: components["schemas"]["AsyncTaskResultSummary"] | null; /** * Purged * @description Whether this item has been permanently removed. @@ -7586,6 +7591,11 @@ export interface components { * @description Whether this resource is currently publicly available to all users. */ published: boolean; + /** + * Purge Task + * @description Summary of the async task purging datasets in this history. Only present when purge is performed via a background task. + */ + purge_task?: components["schemas"]["AsyncTaskResultSummary"] | null; /** * Purged * @description Whether this item has been permanently removed. @@ -9974,6 +9984,11 @@ export interface components { * @description Whether this resource is currently publicly available to all users. */ published?: boolean | null; + /** + * Purge Task + * @description Summary of the async task purging datasets in this history. Only present when purge is performed via a background task. + */ + purge_task?: components["schemas"]["AsyncTaskResultSummary"] | null; /** * Purged * @description Whether this item has been permanently removed. @@ -10213,6 +10228,11 @@ export interface components { * @description Whether this resource is currently publicly available to all users. */ published?: boolean | null; + /** + * Purge Task + * @description Summary of the async task purging datasets in this history. Only present when purge is performed via a background task. + */ + purge_task?: components["schemas"]["AsyncTaskResultSummary"] | null; /** * Purged * @description Whether this item has been permanently removed. @@ -15492,6 +15512,11 @@ export interface components { * @description Whether this resource is currently publicly available to all users. */ published: boolean; + /** + * Purge Task + * @description Summary of the async task purging datasets in this history. Only present when purge is performed via a background task. + */ + purge_task?: components["schemas"]["AsyncTaskResultSummary"] | null; /** * Purged * @description Whether this item has been permanently removed. @@ -15613,6 +15638,11 @@ export interface components { * @description Whether this resource is currently publicly available to all users. */ published: boolean; + /** + * Purge Task + * @description Summary of the async task purging datasets in this history. Only present when purge is performed via a background task. + */ + purge_task?: components["schemas"]["AsyncTaskResultSummary"] | null; /** * Purged * @description Whether this item has been permanently removed. diff --git a/lib/galaxy/celery/tasks.py b/lib/galaxy/celery/tasks.py index 4a3f3128133..61833ba2be6 100644 --- a/lib/galaxy/celery/tasks.py +++ b/lib/galaxy/celery/tasks.py @@ -121,7 +121,7 @@ def purge_datasets( dataset_manager.purge_datasets(request) -@galaxy_task(ignore_result=True, action="purge all datasets in a history") +@galaxy_task(action="purge all datasets in a history") def purge_history_datasets( sa_session: galaxy_scoped_session, dataset_manager: DatasetManager, diff --git a/lib/galaxy/managers/histories.py b/lib/galaxy/managers/histories.py index 6f8bd1200e9..053d456e511 100644 --- a/lib/galaxy/managers/histories.py +++ b/lib/galaxy/managers/histories.py @@ -298,14 +298,16 @@ class HistoryManager(sharable.SharableModelManager[model.History], deletable.Pur request = PurgeHistoryDatasetsTaskRequest(history_id=item.id) user = item.user - purge_history_datasets.delay(request=request, task_user_id=user.id if user else None) + result = purge_history_datasets.delay(request=request, task_user_id=user.id if user else None) else: + result = None for hda in item.datasets: if not hda.purged: self.hda_manager.purge(hda, flush=True, **kwargs) # Now mark the history as purged super().purge(item, flush=flush, **kwargs) + return result # .... current # TODO: make something to bypass the anon user + current history permissions issue diff --git a/lib/galaxy/schema/schema.py b/lib/galaxy/schema/schema.py index 24cbf0e4d92..6a1c45d5f52 100644 --- a/lib/galaxy/schema/schema.py +++ b/lib/galaxy/schema/schema.py @@ -1435,6 +1435,11 @@ class HistorySummary(Model, WithModelClass): tags: TagCollection update_time: datetime = UpdateTimeField preferred_object_store_id: Optional[str] = PreferredObjectStoreIdField + purge_task: Optional["AsyncTaskResultSummary"] = Field( + None, + title="Purge Task", + description="Summary of the async task purging datasets in this history. Only present when purge is performed via a background task.", + ) class HistoryActiveContentCounts(Model): @@ -3997,6 +4002,13 @@ class AsyncTaskResultSummary(Model): ) +HistorySummary.model_rebuild() +HistoryDetailed.model_rebuild() +CustomHistoryView.model_rebuild() +ArchivedHistorySummary.model_rebuild() +ArchivedHistoryDetailed.model_rebuild() +CustomArchivedHistoryView.model_rebuild() + ToolRequestIdField = Field(title="ID", description="Encoded ID of the role") diff --git a/lib/galaxy/webapps/galaxy/services/histories.py b/lib/galaxy/webapps/galaxy/services/histories.py index 3d6a4fb0056..55549c44f21 100644 --- a/lib/galaxy/webapps/galaxy/services/histories.py +++ b/lib/galaxy/webapps/galaxy/services/histories.py @@ -454,10 +454,14 @@ class HistoriesService(ServiceBase, ConsumesModelStores, ServesExportStores): """ history = self.manager.get_mutable(history_id, trans.user, current_history=trans.history) if purge: - self.manager.purge(history) + result = self.manager.purge(history) else: + result = None self.manager.delete(history) - return self._serialize_history(trans, history, serialization_params) + rval = self._serialize_history(trans, history, serialization_params) + if result is not None: + rval["purge_task"] = async_task_summary(result) + return rval def undelete( self, diff --git a/test/integration/test_purge_datasets.py b/test/integration/test_purge_datasets.py index 8f0855ab8e8..753ec4420fd 100644 --- a/test/integration/test_purge_datasets.py +++ b/test/integration/test_purge_datasets.py @@ -4,10 +4,7 @@ from typing import ( Optional, ) -from galaxy_test.base.populators import ( - DatasetPopulator, - wait_on, -) +from galaxy_test.base.populators import DatasetPopulator from galaxy_test.driver import integration_util @@ -95,21 +92,26 @@ class TestPurgeDatasetsIntegration(integration_util.IntegrationTestCase): # Purge the entire history purge_response = self._delete(f"histories/{self.test_history_id}", data={"purge": True}, json=True) self._assert_status_code_is_ok(purge_response) + purge_result = purge_response.json() # Verify history is purged - history_response = self._get(f"histories/{self.test_history_id}").json() - assert history_response["purged"] - assert history_response["deleted"] + assert purge_result["purged"] + assert purge_result["deleted"] - # Verify HDAs are marked as purged + # Verify the response contains a purge_task with an id + assert "purge_task" in purge_result + purge_task = purge_result["purge_task"] + assert "id" in purge_task + purge_task_id = purge_task["id"] + + # Wait for the celery task to complete via the tasks API + self.dataset_populator.wait_on_task_id(purge_task_id) + + # After task completion, HDAs should be purged and files deleted self.dataset_populator.wait_for_purge(self.test_history_id, hda1_id) self.dataset_populator.wait_for_purge(self.test_history_id, hda2_id) - - # Wait for underlying dataset files to be removed from disk. - # With batched history purging, HDAs are marked purged synchronously - # but the actual file deletion happens via a batched celery task. - self._wait_for_file_deleted(dataset_file1) - self._wait_for_file_deleted(dataset_file2) + assert not self._file_exists_on_disk(dataset_file1) + assert not self._file_exists_on_disk(dataset_file2) def _get_underlying_dataset_on_disk(self, hda_id: str) -> Optional[str]: detailed_response = self._get(f"datasets/{hda_id}", admin=True).json() @@ -117,9 +119,3 @@ class TestPurgeDatasetsIntegration(integration_util.IntegrationTestCase): def _file_exists_on_disk(self, filename: Optional[str]) -> bool: return os.path.isfile(filename) if filename else False - - def _wait_for_file_deleted(self, filename: Optional[str], timeout: int = 10): - def _check(): - return True if not self._file_exists_on_disk(filename) else None - - wait_on(_check, f"file {filename} to be deleted", timeout=timeout)