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.
This commit is contained in:
Amruth Pillai
2026-09-29 00:33:29 +02:00
parent cbd47ec1fb
commit 55db11aea8
3 changed files with 14 additions and 3 deletions
+11 -2
View File
@@ -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" });
});
});
+1 -1
View File
@@ -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 })),
@@ -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),
};
},