mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Allow undeleting multiple histories
This commit is contained in:
@@ -5,4 +5,5 @@ export const archivedHistoriesFetcher = fetcher.path("/api/histories/archived").
|
||||
export const deleteHistory = fetcher.path("/api/histories/{history_id}").method("delete").create();
|
||||
export const deleteHistories = fetcher.path("/api/histories/batch/delete").method("post").create();
|
||||
export const undeleteHistory = fetcher.path("/api/histories/deleted/{history_id}/undelete").method("post").create();
|
||||
export const undeleteHistories = fetcher.path("/api/histories/batch/undelete").method("post").create();
|
||||
export const historiesQuery = fetcher.path("/api/histories/query").method("get").create();
|
||||
|
||||
@@ -497,6 +497,10 @@ export interface paths {
|
||||
/** Marks several histories with the given IDs as deleted. */
|
||||
post: operations["batch_delete_api_histories_batch_delete_post"];
|
||||
};
|
||||
"/api/histories/batch/undelete": {
|
||||
/** Marks several histories with the given IDs as undeleted. */
|
||||
post: operations["batch_undelete_api_histories_batch_undelete_post"];
|
||||
};
|
||||
"/api/histories/count": {
|
||||
/** Returns number of histories for the current user. */
|
||||
get: operations["count_api_histories_count_get"];
|
||||
@@ -10372,6 +10376,14 @@ export interface components {
|
||||
*/
|
||||
title?: string | null;
|
||||
};
|
||||
/** UndeleteHistoriesPayload */
|
||||
UndeleteHistoriesPayload: {
|
||||
/**
|
||||
* IDs
|
||||
* @description List of history IDs to be undeleted.
|
||||
*/
|
||||
ids: string[];
|
||||
};
|
||||
/**
|
||||
* UpdateCollectionAttributePayload
|
||||
* @description Contains attributes that can be updated for all elements in a dataset collection.
|
||||
@@ -13889,6 +13901,44 @@ export interface operations {
|
||||
};
|
||||
};
|
||||
};
|
||||
batch_undelete_api_histories_batch_undelete_post: {
|
||||
/** Marks several histories with the given IDs as undeleted. */
|
||||
parameters?: {
|
||||
/** @description View to be passed to the serializer */
|
||||
/** @description Comma-separated list of keys to be passed to the serializer */
|
||||
query?: {
|
||||
view?: string | null;
|
||||
keys?: string | null;
|
||||
};
|
||||
/** @description The user ID that will be used to effectively make this API call. Only admins and designated users can make API calls on behalf of other users. */
|
||||
header?: {
|
||||
"run-as"?: string | null;
|
||||
};
|
||||
};
|
||||
requestBody?: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["UndeleteHistoriesPayload"] | null;
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": (
|
||||
| components["schemas"]["HistoryDetailed"]
|
||||
| components["schemas"]["HistorySummary"]
|
||||
| components["schemas"]["HistoryMinimal"]
|
||||
)[];
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["HTTPValidationError"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
count_api_histories_count_get: {
|
||||
/** Returns number of histories for the current user. */
|
||||
parameters?: {
|
||||
|
||||
@@ -102,6 +102,7 @@ function displayInitialMessage() {
|
||||
* Request grid data
|
||||
*/
|
||||
async function getGridData() {
|
||||
selected.value = new Set();
|
||||
if (props.gridConfig) {
|
||||
try {
|
||||
const offset = props.limit * (currentPage.value - 1);
|
||||
@@ -132,7 +133,6 @@ async function onBatchOperation(operation: BatchOperation, rowDataArray: Array<R
|
||||
await getGridData();
|
||||
operationMessage.value = response.message;
|
||||
operationStatus.value = response.status || "success";
|
||||
selected.value = new Set();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { useEventBus } from "@vueuse/core";
|
||||
|
||||
import { deleteHistories, deleteHistory, historiesQuery, undeleteHistory } from "@/api/histories";
|
||||
import { deleteHistories, deleteHistory, historiesQuery, undeleteHistory, undeleteHistories } from "@/api/histories";
|
||||
import { updateTags } from "@/api/tags";
|
||||
import { useHistoryStore } from "@/stores/historyStore";
|
||||
import Filtering, { contains, equals, expandNameTag, toBool, type ValidFilter } from "@/utils/filtering";
|
||||
@@ -81,10 +81,32 @@ const batch: BatchOperationArray = [
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Restore",
|
||||
icon: faTrashRestore,
|
||||
condition: (data: Array<HistoryEntry>) => data.length > 0 && !data.some((x) => !x.deleted || x.purged),
|
||||
handler: async (data: Array<HistoryEntry>) => {
|
||||
if (confirm(_l(`Are you sure that you want to restore the selected histories?`))) {
|
||||
try {
|
||||
const historyIds = data.map((x) => String(x.id));
|
||||
await undeleteHistories({ ids: historyIds });
|
||||
return {
|
||||
status: "success",
|
||||
message: `Restored ${data.length} histories.`,
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
status: "danger",
|
||||
message: `Failed to restore histories: ${errorMessageAsString(e)}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Purge",
|
||||
icon: faTrash,
|
||||
condition: (data: Array<HistoryEntry>) => data.length > 0 && !data.some((x) => x.deleted),
|
||||
condition: (data: Array<HistoryEntry>) => data.length > 0 && !data.some((x) => x.purged),
|
||||
handler: async (data: Array<HistoryEntry>) => {
|
||||
if (confirm(_l(`Are you sure that you want to permanently delete the selected histories?`))) {
|
||||
try {
|
||||
|
||||
@@ -202,7 +202,9 @@ class HistoryManager(sharable.SharableModelManager, deletable.PurgableManagerMix
|
||||
if show_purged:
|
||||
stmt = stmt.where(self.model_class.purged == true())
|
||||
else:
|
||||
stmt = stmt.where(self.model_class.deleted == (true() if show_deleted else false()))
|
||||
stmt = stmt.where(self.model_class.purged == false()).where(
|
||||
self.model_class.deleted == (true() if show_deleted else false())
|
||||
)
|
||||
|
||||
if include_total_count:
|
||||
total_matches = get_count(trans.sa_session, stmt)
|
||||
|
||||
@@ -147,6 +147,10 @@ class DeleteHistoriesPayload(BaseModel):
|
||||
]
|
||||
|
||||
|
||||
class UndeleteHistoriesPayload(BaseModel):
|
||||
ids: Annotated[List[HistoryIDPathParam], Field(title="IDs", description="List of history IDs to be undeleted.")]
|
||||
|
||||
|
||||
@as_form
|
||||
class CreateHistoryFormData(CreateHistoryPayload):
|
||||
"""Uses Form data instead of JSON"""
|
||||
@@ -398,7 +402,6 @@ class FastAPIHistories:
|
||||
purge = payload.purge
|
||||
results = []
|
||||
for history_id in payload.ids:
|
||||
log.debug(history_id)
|
||||
result = self.service.delete(trans, history_id, serialization_params, purge)
|
||||
results.append(result)
|
||||
return results
|
||||
@@ -415,6 +418,22 @@ class FastAPIHistories:
|
||||
) -> AnyHistoryView:
|
||||
return self.service.undelete(trans, history_id, serialization_params)
|
||||
|
||||
@router.post(
|
||||
"/api/histories/batch/undelete",
|
||||
summary="Marks several histories with the given IDs as undeleted.",
|
||||
)
|
||||
def batch_undelete(
|
||||
self,
|
||||
trans: ProvidesHistoryContext = DependsOnTrans,
|
||||
serialization_params: SerializationParams = Depends(query_serialization_params),
|
||||
payload: Optional[UndeleteHistoriesPayload] = Body(default=None),
|
||||
) -> List[AnyHistoryView]:
|
||||
results = []
|
||||
for history_id in payload.ids:
|
||||
result = self.service.undelete(trans, history_id, serialization_params)
|
||||
results.append(result)
|
||||
return results
|
||||
|
||||
@router.put(
|
||||
"/api/histories/{history_id}",
|
||||
summary="Updates the values for the history with the given ID.",
|
||||
|
||||
Reference in New Issue
Block a user