feat: implement mock API layer and real-time SSE transfer progress si… - #52
Merged
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds a frontend-only mock API layer (via Next.js App Router route handlers) backed by an in-memory mockStore, including simulated transfer jobs with an SSE progress stream. This is intended to let the UI be developed without a running backend and to follow docs/API_CONTRACT.md.
Changes:
- Introduces
frontend/lib/mocks/store.tsas an in-memory mock “backend” for drive items, quota, and simulated transfer jobs (including SSE listeners). - Adds mock Auth routes for login/callback/logout and Source drive connection.
- Adds mock Transfer Jobs routes: create job, read job status, SSE stream, and pause/resume/cancel/retry-failed actions; plus a Source tree browsing route.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 14 comments.
Show a summary per file
| File | Description |
|---|---|
| frontend/lib/mocks/store.ts | In-memory mock store for drive items, quota, and simulated transfer jobs with progress broadcasting. |
| frontend/app/auth/login/route.ts | Mock OAuth entrypoint redirecting into the callback flow. |
| frontend/app/auth/callback/route.ts | Mock OAuth callback; sets a session cookie and marks mock login state. |
| frontend/app/auth/logout/route.ts | Clears the mock session and resets the in-memory store. |
| frontend/app/auth/connect/source/route.ts | Mock connect flow entrypoint for Source drive. |
| frontend/app/auth/callback/drive/route.ts | Shared drive connection callback toggling Source/Target connected flags. |
| frontend/app/api/drive/source/tree/route.ts | Mock Source drive tree listing endpoint. |
| frontend/app/api/jobs/route.ts | Creates a mock transfer job and starts background progress simulation. |
| frontend/app/api/jobs/[id]/route.ts | Retrieves a mock transfer job status payload. |
| frontend/app/api/jobs/[id]/stream/route.ts | SSE endpoint streaming mock job progress events. |
| frontend/app/api/jobs/[id]/pause/route.ts | Pauses a running mock job. |
| frontend/app/api/jobs/[id]/resume/route.ts | Resumes a paused mock job. |
| frontend/app/api/jobs/[id]/cancel/route.ts | Cancels a running/paused/pending mock job. |
| frontend/app/api/jobs/[id]/retry-failed/route.ts | Retries failed items for a failed/completed-with-errors mock job. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (item.type === "file") totalFiles++; | ||
| else { | ||
| totalFolders++; | ||
| // Add children recursively in simulation |
Comment on lines
+196
to
+206
| if (job && (job.status === "running" || job.status === "paused" || job.status === "pending")) { | ||
| job.status = "cancelled"; | ||
| job.updatedAt = new Date().toISOString(); | ||
| this.broadcastToJob(id, "done", { | ||
| status: "cancelled", | ||
| filesCompleted: job.filesCompleted, | ||
| filesFailed: job.filesFailed, | ||
| totalFiles: job.totalFiles | ||
| }); | ||
| return true; | ||
| } |
Comment on lines
+303
to
+314
| } else { | ||
| // Simulation completed | ||
| clearInterval(interval); | ||
| job.status = job.filesFailed > 0 ? "completed_with_errors" : "completed"; | ||
| job.updatedAt = new Date().toISOString(); | ||
| this.broadcastToJob(id, "done", { | ||
| status: job.status, | ||
| filesCompleted: job.filesCompleted, | ||
| filesFailed: job.filesFailed, | ||
| totalFiles: job.totalFiles | ||
| }); | ||
| } |
Comment on lines
+9
to
+22
| // Validate parameters | ||
| if (!transferMode || !conflictPolicy || !sourceItemIds || !targetParentId) { | ||
| return NextResponse.json( | ||
| { error: "bad_request", message: "Missing required parameters in request body" }, | ||
| { status: 400 } | ||
| ); | ||
| } | ||
|
|
||
| if (transferMode !== "COPY" && transferMode !== "MOVE") { | ||
| return NextResponse.json( | ||
| { error: "invalid_mode", message: "transferMode must be COPY or MOVE" }, | ||
| { status: 400 } | ||
| ); | ||
| } |
| import { cookies } from "next/headers"; | ||
| import { mockStore } from "@/lib/mocks/store"; | ||
|
|
||
| export async function POST(request: NextRequest) { |
Comment on lines
+4
to
+9
| export async function POST( | ||
| request: NextRequest, | ||
| context: { params: Promise<{ id: string }> } | ||
| ) { | ||
| const { id } = await context.params; | ||
| const itemsRequeued = mockStore.retryFailedJob(id); |
Comment on lines
+4
to
+10
| export async function GET( | ||
| request: NextRequest, | ||
| context: { params: Promise<{ id: string }> } | ||
| ) { | ||
| const { id } = await context.params; | ||
| const job = mockStore.getJob(id); | ||
|
|
Comment on lines
+13
to
+15
| const searchParams = request.nextUrl.searchParams; | ||
| const folderId = searchParams.get("folderId") || undefined; | ||
|
|
Comment on lines
+3
to
+7
| export async function GET(request: NextRequest) { | ||
| // Redirect to callback/drive simulating permission authorization for Source drive | ||
| const targetUrl = new URL("/auth/callback/drive?state=source", request.url); | ||
| return NextResponse.redirect(targetUrl); | ||
| } |
Comment on lines
+81
to
+87
| // Get target directories (only folders) | ||
| public getTargetFolders(parentId?: string) { | ||
| const targetParentId = parentId || "root"; | ||
| const breadcrumb = this.getBreadcrumbs(this.targetItems, targetParentId); | ||
| const items = this.targetItems.filter(item => item.parentId === targetParentId && item.type === "folder"); | ||
| return { folderId: targetParentId, breadcrumb, items }; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR implements the mock API handlers matching
docs/API_CONTRACT.mdresponses, along with an in-memory session database and background transfer progress simulation.Closes #9
Changes:
frontend/lib/mocks/store.ts): Simulates drive connection status, tree structures, folder creation, quota availability, and background transfer task progress./auth/login,/auth/callback,/auth/logout,/auth/connect/*,/auth/callback/driveto simulate Google OAuth./api/drive/source/tree,/api/drive/target/folders,/api/drive/target/quotafor listing and folder creation./api/jobs(POST to create),/api/jobs/[id](GET status), and/api/jobs/[id]/stream(Server-Sent Events progress stream)./pause,/resume,/cancel,/retry-failedto control the background simulation engine.