Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions apps/server/src/infrastructure/api-clients/sources-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,9 @@ export function restoreSource(id: string, data: unknown) {
* @returns Import result
*/
export async function importSourceZip(id: string, file: File) {
return orpc.sources.importZip({ id, file });
return orpc.sources.enqueueImport({ id, mode: "zip", file });
}

export async function importSourceNdjson(id: string, file: File) {
return await orpc.sources.importNdjson({
id,
file,
});
return orpc.sources.enqueueImport({ id, mode: "json", file });
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import {
fetchSourceDump,
importSourceNdjson,
importSourceZip,
restoreSource,
} from "~/infrastructure/api-clients/sources-api";
Expand All @@ -10,7 +11,7 @@ vi.mock("~/infrastructure/api-clients/orpc-client", () => ({
orpc: {
sources: {
enqueueExport: vi.fn(),
importZip: vi.fn(),
enqueueImport: vi.fn(),
restore: vi.fn(),
},
jobs: { downloadArtifact: vi.fn(), get: vi.fn() },
Expand Down Expand Up @@ -88,18 +89,41 @@ describe("Sources API Client Extensions", () => {
expect(await result.text()).toBe("zip content");
});

it("should upload imports through oRPC", async () => {
it("should enqueue TAR imports through oRPC", async () => {
const id = "test-source-id";
const mockFile = new File(["zip content"], "test.zip", {
type: "application/zip",
});
const mockResponse = { importedCount: 1 };
((orpc.sources as any).importZip as any).mockResolvedValue(mockResponse);
const mockResponse = { id: "restore-job-id" };
((orpc.sources as any).enqueueImport as any).mockResolvedValue(
mockResponse,
);

const result = await importSourceZip(id, mockFile);

expect((orpc.sources as any).importZip).toHaveBeenCalledWith({
expect((orpc.sources as any).enqueueImport).toHaveBeenCalledWith({
id,
mode: "zip",
file: mockFile,
});
expect(result).toEqual(mockResponse);
});

it("should enqueue NDJSON imports through oRPC", async () => {
const id = "test-source-id";
const mockFile = new File(["{}\n"], "test.ndjson", {
type: "application/x-ndjson",
});
const mockResponse = { id: "restore-job-id" };
((orpc.sources as any).enqueueImport as any).mockResolvedValue(
mockResponse,
);

const result = await importSourceNdjson(id, mockFile);

expect((orpc.sources as any).enqueueImport).toHaveBeenCalledWith({
id,
mode: "json",
file: mockFile,
});
expect(result).toEqual(mockResponse);
Expand Down
7 changes: 2 additions & 5 deletions apps/tauri/src/infrastructure/api-clients/sources-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,11 @@ export async function restoreSource(
}

export async function importSourceZip(id: string, file: File) {
return client.sources.importZip({ id, file });
return client.sources.enqueueImport({ id, mode: "zip", file });
}

export async function importSourceNdjson(id: string, file: File) {
return client.sources.importNdjson({
id,
file,
});
return client.sources.enqueueImport({ id, mode: "json", file });
}
export function parseRestoreFile(file: File): Promise<unknown[]> {
return file.text().then((text) => JSON.parse(text));
Expand Down
35 changes: 9 additions & 26 deletions packages/ui/src/hooks/use-source-media-page.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Character } from "@solid-imager/core/domain/characters/schemas";
import type { Ip } from "@solid-imager/core/domain/ips/schemas";
import type { JobDto } from "@solid-imager/core/domain/jobs/schemas";
import type {
Author,
DownloadItem,
Expand Down Expand Up @@ -122,24 +123,8 @@ export type SourceMediaPageActions = {
errors: string[];
cancelled?: boolean;
}>;
importSourceZip: (
sourceId: string,
file: File,
) => Promise<{
success: boolean;
importedCount: number;
skippedCount: number;
errors: string[];
message: string;
}>;
importSourceNdjson?: (
sourceId: string,
file: File,
) => Promise<{
importedCount: number;
skippedCount: number;
errors: string[];
}>;
importSourceZip: (sourceId: string, file: File) => Promise<JobDto>;
importSourceNdjson?: (sourceId: string, file: File) => Promise<JobDto>;
parseRestoreFile?: (file: File) => Promise<unknown>;
};

Expand Down Expand Up @@ -642,28 +627,26 @@ export function useSourceMediaPage(
});

if (strategies.includes("tar")) {
toast.loading("Importing TAR dump...", { id: "restore-toast" });
const result = await actions.importSourceZip(sourceId, file);
toast.loading("Queueing TAR restore...", { id: "restore-toast" });
const job = await actions.importSourceZip(sourceId, file);
toast.success(
`Import complete: ${result.importedCount} items imported.`,
`TAR restore queued (${job.id.slice(0, 8)}). Track it in Jobs.`,
{
id: "restore-toast",
},
);
refreshMediaQuery();
return;
}

if (strategies[0] === "ndjson" && actions.importSourceNdjson) {
toast.loading("Importing NDJSON dump...", { id: "restore-toast" });
const result = await actions.importSourceNdjson(sourceId, file);
toast.loading("Queueing NDJSON restore...", { id: "restore-toast" });
const job = await actions.importSourceNdjson(sourceId, file);
toast.success(
`Import complete: ${result.importedCount} items imported.`,
`NDJSON restore queued (${job.id.slice(0, 8)}). Track it in Jobs.`,
{
id: "restore-toast",
},
);
refreshMediaQuery();
return;
}

Expand Down