Allow undeleting multiple histories

This commit is contained in:
guerler
2024-01-15 15:42:57 +03:00
parent 7a396e61e8
commit 75152d5449
6 changed files with 99 additions and 5 deletions
+1
View File
@@ -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();
+50
View File
@@ -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?: {
+1 -1
View File
@@ -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 {
+3 -1
View File
@@ -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)
+20 -1
View File
@@ -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.",