Split browser workflows from presentation
This commit is contained in:
parent
c9642f4067
commit
0c2d992224
1 changed files with 1683 additions and 854 deletions
89
web/src/workflows/recursive-delete.ts
Normal file
89
web/src/workflows/recursive-delete.ts
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
import type {
|
||||
DeletePlanResponse,
|
||||
ProductEndpoint,
|
||||
Root,
|
||||
SessionResponse
|
||||
} from '../generated/contract.ts';
|
||||
import { unfinishedDeleteItems, type PendingDelete } from '../mutations.ts';
|
||||
import { requireValue } from '../transport.ts';
|
||||
import type { RecursiveDeletePersistence } from './recursive-delete-persistence.ts';
|
||||
|
||||
export type RecursiveDeleteResult = {
|
||||
deleted: number;
|
||||
pages: number;
|
||||
};
|
||||
|
||||
export class RecursiveDeletePageLimitError extends Error {
|
||||
readonly pages: number;
|
||||
|
||||
constructor(pages: number) {
|
||||
super(`Recursive delete stopped after ${pages} pages; run it again to continue.`);
|
||||
this.name = 'RecursiveDeletePageLimitError';
|
||||
this.pages = pages;
|
||||
}
|
||||
}
|
||||
|
||||
export function savedRecursiveDelete(
|
||||
persistence: RecursiveDeletePersistence
|
||||
): PendingDelete | null {
|
||||
return persistence.read();
|
||||
}
|
||||
|
||||
export async function runRecursiveDelete(
|
||||
client: ProductEndpoint,
|
||||
persistence: RecursiveDeletePersistence,
|
||||
session: SessionResponse,
|
||||
root: Root,
|
||||
prefix: string,
|
||||
nextOperationId: () => string,
|
||||
pageLimit = 100
|
||||
): Promise<RecursiveDeleteResult> {
|
||||
let deleted = 0;
|
||||
let completedPages = 0;
|
||||
let continuation: string | null = null;
|
||||
for (let page = 0; page < pageLimit; page += 1) {
|
||||
let items = persistence.load(root, prefix);
|
||||
let nextContinuation: string | null = null;
|
||||
if (items.length === 0) {
|
||||
const plan: DeletePlanResponse = requireValue(
|
||||
await client.deletePlan({
|
||||
root,
|
||||
path: prefix,
|
||||
...(continuation === null ? {} : { continuation })
|
||||
})
|
||||
);
|
||||
if (plan.paths.length === 0) {
|
||||
if (plan.continuation !== null) {
|
||||
continuation = plan.continuation;
|
||||
continue;
|
||||
}
|
||||
persistence.clear();
|
||||
return { deleted, pages: completedPages };
|
||||
}
|
||||
nextContinuation = plan.continuation;
|
||||
items = plan.paths.map((path) => ({
|
||||
path,
|
||||
operation_id: nextOperationId()
|
||||
}));
|
||||
persistence.save(root, prefix, items);
|
||||
}
|
||||
|
||||
const response = requireValue(
|
||||
await client.deleteBatch({
|
||||
body: { root, csrf: session.csrf, items }
|
||||
})
|
||||
);
|
||||
const unfinished = unfinishedDeleteItems(response.results, nextOperationId);
|
||||
deleted += response.results.length - unfinished.length;
|
||||
if (unfinished.length > 0) {
|
||||
persistence.save(root, prefix, unfinished);
|
||||
throw new Error(
|
||||
`${unfinished.length} objects remain. Pending operations keep their IDs; terminal failures restart with new IDs.`
|
||||
);
|
||||
}
|
||||
persistence.clear();
|
||||
completedPages += 1;
|
||||
continuation = nextContinuation;
|
||||
}
|
||||
throw new RecursiveDeletePageLimitError(pageLimit);
|
||||
}
|
||||
Loading…
Reference in a new issue