86 lines
2.2 KiB
TypeScript
86 lines
2.2 KiB
TypeScript
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 {
|
|
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,
|
|
};
|
|
}
|