89 lines
2.6 KiB
TypeScript
89 lines
2.6 KiB
TypeScript
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);
|
|
}
|