feat(atlassian): implement milestone M4 - packaging and doc sync
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
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,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user