import { readWorkspaceFile } from "./files.js"; import { sendJsonRequest } from "./http.js"; import type { AtlassianConfig, CommandOutput, FetchLike } from "./types.js"; const JIRA_ALLOWED_PREFIXES = ["/rest/api/3/"] as const; const CONFLUENCE_ALLOWED_PREFIXES = ["/wiki/api/v2/", "/wiki/rest/api/"] as const; type RawInput = { product: "jira" | "confluence"; method: string; path: string; bodyFile?: string; cwd: string; dryRun?: boolean; }; function getAllowedPrefixes(product: RawInput["product"]) { return product === "jira" ? JIRA_ALLOWED_PREFIXES : CONFLUENCE_ALLOWED_PREFIXES; } function buildUrl(config: AtlassianConfig, product: RawInput["product"], path: string) { const baseUrl = product === "jira" ? config.jiraBaseUrl : config.confluenceBaseUrl; return new URL(path, `${baseUrl}/`).toString(); } function validateMethod(method: string): asserts method is "GET" | "POST" | "PUT" { if (!["GET", "POST", "PUT"].includes(method)) { throw new Error("raw only allows GET, POST, and PUT"); } } function validatePath(product: RawInput["product"], path: string) { const allowedPrefixes = getAllowedPrefixes(product); if (!allowedPrefixes.some((prefix) => path.startsWith(prefix))) { throw new Error(`raw path is not allowed for ${product}: ${path}`); } } async function readRawBody(bodyFile: string | undefined, cwd: string) { if (!bodyFile) { return undefined; } const contents = await readWorkspaceFile(bodyFile, cwd); return JSON.parse(contents) as unknown; } export async function runRawCommand( config: AtlassianConfig, fetchImpl: FetchLike | undefined, input: RawInput, ): Promise> { validateMethod(input.method); validatePath(input.product, input.path); const body = await readRawBody(input.bodyFile, input.cwd); const request = { method: input.method, url: buildUrl(config, input.product, input.path), ...(body === undefined ? {} : { body }), }; if (input.dryRun) { return { ok: true, dryRun: true, data: request, }; } const data = await sendJsonRequest({ config, fetchImpl, url: request.url, method: input.method, body, errorPrefix: "Raw request failed", }); return { ok: true, data, }; }