From b78ffc6ea1460db80f0139406ab8ef370bfd51f3 Mon Sep 17 00:00:00 2001 From: Jarvis AI Team Date: Mon, 4 May 2026 21:24:07 +0800 Subject: [PATCH] feat: support multiple URLs for GET endpoint (issue #187) --- src/handlers/start/api/helpers/types.ts | 11 ++++- src/handlers/start/api/public-api.ts | 16 +++++++- src/handlers/start/api/validate-or-execute.ts | 40 +++++++++++++++++++ 3 files changed, 64 insertions(+), 3 deletions(-) diff --git a/src/handlers/start/api/helpers/types.ts b/src/handlers/start/api/helpers/types.ts index 91681e49..55e65c47 100644 --- a/src/handlers/start/api/helpers/types.ts +++ b/src/handlers/start/api/helpers/types.ts @@ -13,7 +13,8 @@ export const startQueryParamSchema = T.Object( return parsed; }) .Encode((val) => val.toString()), - issueUrl: T.String({ minLength: 1 }), + issueUrl: T.Optional(T.String({ minLength: 1 })), + issueUrls: T.Optional(T.Array(T.String({ minLength: 1 }))), environment: T.Optional(T.Union([T.Literal("development"), T.Literal("production")])), }, { @@ -29,6 +30,14 @@ export type IssueUrlParts = { issue_number: number; }; +export type MultiUrlResult = { + issueUrl: string; + ok: boolean; + computed?: StartEligibilityResult["computed"]; + warnings?: LogReturn[] | null; + reasons?: string[] | null; +}; + export type DatabaseUser = { id: number | string; wallet_id: number | null; diff --git a/src/handlers/start/api/public-api.ts b/src/handlers/start/api/public-api.ts index 9e207099..adcb58c4 100644 --- a/src/handlers/start/api/public-api.ts +++ b/src/handlers/start/api/public-api.ts @@ -6,7 +6,7 @@ import { extractJwtFromHeader, verifyJwt } from "./helpers/auth"; import { buildShallowContextObject } from "./helpers/context-builder"; import { fetchMergedPluginSettings } from "./helpers/get-plugin-config"; import { StartQueryParams, startQueryParamSchema } from "./helpers/types"; -import { handleValidateOrExecute } from "./validate-or-execute"; +import { handleMultiUrlValidateOrExecute, handleValidateOrExecute } from "./validate-or-execute"; /** * Main handler for the public start API endpoint. @@ -62,7 +62,7 @@ export async function handlePublicStart(honoCtx: HonoContext, env: Env, logger: // Validate environment and parse request query params const params = await validateQueryParams(honoCtx, logger); if (params instanceof Response) return params; - const { issueUrl, userId, environment } = params; + const { issueUrl, issueUrls, userId, environment } = params; // Build context and load merged plugin settings from org/repo config const context = await buildShallowContextObject({ @@ -72,6 +72,18 @@ export async function handlePublicStart(honoCtx: HonoContext, env: Env, logger: logger, }); + // Handle multi-URL case + if (issueUrls && issueUrls.length > 0) { + context.config = await fetchMergedPluginSettings({ + env, + issueUrl: issueUrls[0], + logger, + environment, + jwt, + }); + return await handleMultiUrlValidateOrExecute({ context, mode, issueUrls, jwt }); + } + context.config = await fetchMergedPluginSettings({ env, issueUrl, diff --git a/src/handlers/start/api/validate-or-execute.ts b/src/handlers/start/api/validate-or-execute.ts index 9a4f0d6b..27109865 100644 --- a/src/handlers/start/api/validate-or-execute.ts +++ b/src/handlers/start/api/validate-or-execute.ts @@ -6,6 +6,32 @@ import { performAssignment } from "../perform-assignment"; import { createCommand, createPayload, ShallowContext } from "./helpers/context-builder"; import { createRepoOctokit, createUserOctokit } from "./helpers/octokit"; import { parseIssueUrl } from "./helpers/parsers"; +import type { MultiUrlResult } from "./helpers/types"; + +/** + * Handles multiple issue URLs and returns results for each. + */ +export async function handleMultiUrlValidateOrExecute({ + context, + mode, + issueUrls, + jwt, +}: { + context: ShallowContext; + mode: "validate" | "execute"; + issueUrls: string[]; + jwt: string; +}): Promise { + const results: MultiUrlResult[] = []; + + for (const issueUrl of issueUrls) { + const result = await handleSingleUrl({ context, mode, issueUrl, jwt }); + const body = await result.clone().json(); + results.push({ issueUrl, ...body }); + } + + return Response.json({ results }, { status: 200 }); +} /** * Handles the validate or execute flow for a specific issue. @@ -21,6 +47,20 @@ export async function handleValidateOrExecute({ mode: "validate" | "execute"; issueUrl: string; jwt: string; +}): Promise { + return handleSingleUrl({ context, mode, issueUrl, jwt }); +} + +async function handleSingleUrl({ + context, + mode, + issueUrl, + jwt, +}: { + context: ShallowContext; + mode: "validate" | "execute"; + issueUrl: string; + jwt: string; }): Promise { const { owner, repo, issue_number: issueNumber } = parseIssueUrl(issueUrl, context.logger); let issue, repository, organization;