feat(FN-4378): complete Step 4 — plumb github issue action through API
Fusion-Task-Id: FN-4378 Fusion-Task-Lineage: d3867ba8-f852-41e3-9db0-584c0ffc23b1
This commit is contained in:
@@ -564,6 +564,18 @@ describe("Git Management API", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("sends githubIssueAction when requested", async () => {
|
||||
const deletedTask: Task = { ...FAKE_DETAIL, column: "done" };
|
||||
globalThis.fetch = vi.fn().mockReturnValue(mockFetchResponse(true, deletedTask));
|
||||
|
||||
await deleteTask("FN-001", undefined, { githubIssueAction: "delete" });
|
||||
|
||||
expect(globalThis.fetch).toHaveBeenCalledWith("/api/tasks/FN-001?githubIssueAction=delete", {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
method: "DELETE",
|
||||
});
|
||||
});
|
||||
|
||||
it("throws ApiRequestError on error and preserves details payload", async () => {
|
||||
globalThis.fetch = vi.fn().mockReturnValue(
|
||||
mockFetchResponse(
|
||||
|
||||
@@ -77,7 +77,7 @@ import type {
|
||||
TaskIdIntegrityReport,
|
||||
} from "@fusion/core";
|
||||
import type { PlanningQuestion, PlanningSummary } from "@fusion/core";
|
||||
import type { ScheduledTask, ScheduledTaskCreateInput, ScheduledTaskUpdateInput, AutomationRunResult, Routine, RoutineCreateInput, RoutineUpdateInput, RoutineExecutionResult } from "@fusion/core";
|
||||
import type { GithubIssueAction, ScheduledTask, ScheduledTaskCreateInput, ScheduledTaskUpdateInput, AutomationRunResult, Routine, RoutineCreateInput, RoutineUpdateInput, RoutineExecutionResult } from "@fusion/core";
|
||||
import type { DiscoveredSkill, CatalogEntry, CatalogFetchResult, ToggleSkillResult, SkillContent, SkillFileEntry } from "@fusion/dashboard";
|
||||
import type { MilestoneValidationTelemetry, MissionInterviewDraftSummary } from "../components/mission-types";
|
||||
import type {
|
||||
@@ -106,6 +106,7 @@ export class ApiRequestError extends Error {
|
||||
|
||||
export interface DeleteTaskOptions {
|
||||
removeDependencyReferences?: boolean;
|
||||
githubIssueAction?: GithubIssueAction;
|
||||
}
|
||||
|
||||
function looksLikeHtml(body: string): boolean {
|
||||
@@ -455,6 +456,9 @@ export function deleteTask(id: string, projectId?: string, options?: DeleteTaskO
|
||||
if (options?.removeDependencyReferences) {
|
||||
search.set("removeDependencyReferences", "true");
|
||||
}
|
||||
if (options?.githubIssueAction) {
|
||||
search.set("githubIssueAction", options.githubIssueAction);
|
||||
}
|
||||
|
||||
const suffix = search.size > 0 ? `?${search.toString()}` : "";
|
||||
return api<Task>(withProjectId(`/tasks/${id}${suffix}`, projectId), { method: "DELETE" });
|
||||
|
||||
@@ -890,7 +890,10 @@ describe("DELETE /tasks/:id", () => {
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body.id).toBe("KB-001");
|
||||
expect(store.deleteTask).toHaveBeenCalledWith("KB-001", { removeDependencyReferences: false });
|
||||
expect(store.deleteTask).toHaveBeenCalledWith("KB-001", {
|
||||
removeDependencyReferences: false,
|
||||
githubIssueAction: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it("returns structured 409 conflict when delete is blocked by dependents", async () => {
|
||||
@@ -917,7 +920,31 @@ describe("DELETE /tasks/:id", () => {
|
||||
const res = await REQUEST(buildApp(), "DELETE", "/api/tasks/KB-001?removeDependencyReferences=true");
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(store.deleteTask).toHaveBeenCalledWith("KB-001", { removeDependencyReferences: true });
|
||||
expect(store.deleteTask).toHaveBeenCalledWith("KB-001", {
|
||||
removeDependencyReferences: true,
|
||||
githubIssueAction: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it.each(["close", "delete", "leave", "auto"] as const)("forwards githubIssueAction=%s", async (githubIssueAction) => {
|
||||
const deletedTask = { ...FAKE_TASK_DETAIL, id: "KB-001" };
|
||||
(store.deleteTask as ReturnType<typeof vi.fn>).mockResolvedValue(deletedTask);
|
||||
|
||||
const res = await REQUEST(buildApp(), "DELETE", `/api/tasks/KB-001?githubIssueAction=${githubIssueAction}`);
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(store.deleteTask).toHaveBeenCalledWith("KB-001", {
|
||||
removeDependencyReferences: false,
|
||||
githubIssueAction,
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects invalid githubIssueAction values", async () => {
|
||||
const res = await REQUEST(buildApp(), "DELETE", "/api/tasks/KB-001?githubIssueAction=bad-value");
|
||||
|
||||
expect(res.status).toBe(400);
|
||||
expect(res.body.error).toContain("githubIssueAction must be one of: close, delete, leave, auto");
|
||||
expect(store.deleteTask).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createReadStream } from "node:fs";
|
||||
import type { TaskStore, Task, TaskDetail, Column, TaskReviewData, TaskReviewItem, TaskReviewSummary } from "@fusion/core";
|
||||
import type { TaskStore, Task, TaskDetail, Column, TaskReviewData, TaskReviewItem, TaskReviewSummary, GithubIssueAction } from "@fusion/core";
|
||||
import {
|
||||
COLUMNS,
|
||||
TASK_PRIORITIES,
|
||||
@@ -2229,7 +2229,16 @@ export function registerTaskWorkflowRoutes(ctx: ApiRoutesContext, deps: TaskWork
|
||||
const { store: scopedStore } = await getProjectContext(req);
|
||||
const removeDependencyReferences = req.query.removeDependencyReferences === "1"
|
||||
|| req.query.removeDependencyReferences === "true";
|
||||
const task = await scopedStore.deleteTask(req.params.id, { removeDependencyReferences });
|
||||
const githubIssueActionRaw = req.query.githubIssueAction;
|
||||
const githubIssueActionValues: readonly GithubIssueAction[] = ["close", "delete", "leave", "auto"];
|
||||
let githubIssueAction: GithubIssueAction | undefined;
|
||||
if (typeof githubIssueActionRaw === "string") {
|
||||
if (!githubIssueActionValues.includes(githubIssueActionRaw as GithubIssueAction)) {
|
||||
throw badRequest("githubIssueAction must be one of: close, delete, leave, auto");
|
||||
}
|
||||
githubIssueAction = githubIssueActionRaw as GithubIssueAction;
|
||||
}
|
||||
const task = await scopedStore.deleteTask(req.params.id, { removeDependencyReferences, githubIssueAction });
|
||||
res.json(task);
|
||||
} catch (err: unknown) {
|
||||
if (err instanceof ApiError) {
|
||||
|
||||
Reference in New Issue
Block a user