From 55db11aea88149f100019ff2cba46a2ec33a122a Mon Sep 17 00:00:00 2001 From: Amruth Pillai Date: Tue, 29 Sep 2026 00:33:29 +0200 Subject: [PATCH] feat(api): include applications in the account export Export everything downloads documents and applications together, so the account export now returns the user's applications, without the owner id. --- packages/api/src/features/auth/export.test.ts | 13 +++++++++++-- packages/api/src/features/auth/router.ts | 2 +- packages/api/src/features/auth/service.ts | 2 ++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/api/src/features/auth/export.test.ts b/packages/api/src/features/auth/export.test.ts index 8ada35661..69c1bbb3d 100644 --- a/packages/api/src/features/auth/export.test.ts +++ b/packages/api/src/features/auth/export.test.ts @@ -8,6 +8,7 @@ vi.mock("@reactive-resume/db/schema", () => ({ user: { id: "user.id" }, resume: { userId: "resume.userId" }, coverLetter: { userId: "coverLetter.userId" }, + application: { userId: "application.userId" }, })); vi.mock("drizzle-orm", () => ({ eq: (column: unknown, value: unknown) => ({ column, value }) })); vi.mock("@reactive-resume/env/server", () => ({ env: {} })); @@ -15,7 +16,7 @@ vi.mock("../storage/service", () => ({ getStorageService: vi.fn() })); const { authService } = await import("./service"); describe("account backup", () => { - it("includes owned independent cover letters alongside embedded resume letters", async () => { + it("includes owned cover letters and applications alongside resumes", async () => { const letter = { id: "letter", name: "Saved", @@ -35,7 +36,13 @@ describe("account backup", () => { createdAt: new Date(), updatedAt: new Date(), }; - for (const rows of [[{ id: "owner", name: "Owner" }], [{ id: "resume", data: defaultResumeData }], [letter]]) { + const application = { id: "application", userId: "owner", company: "Lumen", role: "Designer" }; + for (const rows of [ + [{ id: "owner", name: "Owner" }], + [{ id: "resume", data: defaultResumeData }], + [letter], + [application], + ]) { mocks.select.mockReturnValueOnce({ from: () => ({ where: (predicate: unknown) => { @@ -47,6 +54,8 @@ describe("account backup", () => { } const exported = await authService.exportData({ userId: "owner" }); expect(exported).toMatchObject({ coverLetters: [letter], resumes: [{ id: "resume" }] }); + // Applications come along, without the owner's id. + expect(exported.applications).toEqual([{ id: "application", company: "Lumen", role: "Designer" }]); expect(mocks.predicates).toContainEqual({ column: "coverLetter.userId", value: "owner" }); }); }); diff --git a/packages/api/src/features/auth/router.ts b/packages/api/src/features/auth/router.ts index 6c568bf95..c42a3d7b5 100644 --- a/packages/api/src/features/auth/router.ts +++ b/packages/api/src/features/auth/router.ts @@ -26,7 +26,7 @@ export const authRouter = { operationId: "exportAccountData", summary: "Export user account data", description: - "Returns a JSON-serializable export of the authenticated user's data, including their public profile fields, resumes, and independent cover letters. Images remain URL references. Secrets such as password hashes, tokens, and API keys are never included. Requires authentication.", + "Returns a JSON-serializable export of the authenticated user's data, including their public profile fields, resumes, independent cover letters and job applications. Images remain URL references. Secrets such as password hashes, tokens, and API keys are never included. Requires authentication.", successDescription: "The user's exported account data.", }) .handler(({ context }) => authService.exportData({ userId: context.user.id })), diff --git a/packages/api/src/features/auth/service.ts b/packages/api/src/features/auth/service.ts index 3db6ed51b..d66158496 100644 --- a/packages/api/src/features/auth/service.ts +++ b/packages/api/src/features/auth/service.ts @@ -62,11 +62,13 @@ export const authService = { .where(eq(schema.resume.userId, input.userId)); const coverLetters = await db.select().from(schema.coverLetter).where(eq(schema.coverLetter.userId, input.userId)); + const applications = await db.select().from(schema.application).where(eq(schema.application.userId, input.userId)); return { exportedAt: new Date().toISOString(), user: userRecord, resumes, coverLetters: coverLetters.map((letter) => coverLetterSchema.parse(letter)), + applications: applications.map(({ userId: _userId, ...application }) => application), }; },