This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
// "Export my data": every installed module's share of the account, composed into one document.
|
||||
//
|
||||
// Each module that keeps something about the person contributes an export to kit-tenancy's
|
||||
// `tenancy.account` point -- this module's own rows among them -- and every one of them is a section.
|
||||
// A module that fails is a `failed` section and the export is `complete: false`, naming it; a module
|
||||
// that can erase but not export is `not_exported`. Nothing is left out without saying so (joint
|
||||
// contract §12).
|
||||
|
||||
import { AppError, notFound } from "@conusai-kit/core"
|
||||
import { ACCOUNT_EXPORT_FORMAT } from "../contract/constants.js"
|
||||
import type { AccountExport, ExportSection } from "../contract/procedures.js"
|
||||
import type { AccountDeps } from "./ports.js"
|
||||
|
||||
/** The problem code of a refusal, and whether trying again later may help. Never its detail. */
|
||||
export function refusalOf(error: unknown): { code: string; retryable: boolean } {
|
||||
if (error instanceof AppError) return { code: error.code, retryable: error.status >= 500 || error.status === 429 }
|
||||
return { code: "unavailable", retryable: true }
|
||||
}
|
||||
|
||||
export async function exportAccount(deps: AccountDeps, userId: string): Promise<AccountExport> {
|
||||
if ((await deps.store.emailOf(userId)) === null) throw notFound("no such user")
|
||||
const sections = await Promise.all(
|
||||
deps.contributions.map(async (contribution): Promise<[string, ExportSection]> => {
|
||||
if (!contribution.export) return [contribution.key, { status: "not_exported" }]
|
||||
try {
|
||||
const data = await contribution.export({
|
||||
subject: userId,
|
||||
container: deps.container,
|
||||
...(deps.signal ? { signal: deps.signal } : {}),
|
||||
})
|
||||
return [contribution.key, { status: "ok", data }]
|
||||
} catch (error) {
|
||||
return [contribution.key, { status: "failed", ...refusalOf(error) }]
|
||||
}
|
||||
}),
|
||||
)
|
||||
const failed = sections.filter(([, section]) => section.status === "failed").map(([key]) => key)
|
||||
return {
|
||||
format: ACCOUNT_EXPORT_FORMAT,
|
||||
exportedAt: new Date(deps.now()).toISOString(),
|
||||
subject: userId,
|
||||
complete: sections.every(([, section]) => section.status === "ok"),
|
||||
failed,
|
||||
sections: Object.fromEntries(sections),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user