82 lines
2.4 KiB
TypeScript
82 lines
2.4 KiB
TypeScript
// ⚠️ GENERATED FILE – do not edit directly. Edit the canonical source in skills/atlassian/shared/scripts/ and run `pnpm run sync:pi`.
|
||
import { dryRunResponse } from "./command-helpers.js";
|
||
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<CommandOutput<unknown>> {
|
||
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 dryRunResponse(request);
|
||
|
||
const data = await sendJsonRequest({
|
||
config,
|
||
fetchImpl,
|
||
url: request.url,
|
||
method: input.method,
|
||
body,
|
||
errorPrefix: "Raw request failed",
|
||
});
|
||
|
||
return {
|
||
ok: true,
|
||
data,
|
||
};
|
||
}
|