feat(atlassian): implement milestone M3 - confluence and safety controls
This commit is contained in:
@@ -3,10 +3,12 @@ import { pathToFileURL } from "node:url";
|
||||
|
||||
import { Command } from "commander";
|
||||
|
||||
import { createConfluenceClient } from "./confluence.js";
|
||||
import { loadConfig } from "./config.js";
|
||||
import { readWorkspaceFile } from "./files.js";
|
||||
import { createJiraClient } from "./jira.js";
|
||||
import { writeOutput } from "./output.js";
|
||||
import { runRawCommand } from "./raw.js";
|
||||
import type { FetchLike, OutputFormat, Writer } from "./types.js";
|
||||
|
||||
type CliContext = {
|
||||
@@ -28,6 +30,7 @@ function createRuntime(context: CliContext) {
|
||||
const stderr = context.stderr ?? process.stderr;
|
||||
let configCache: ReturnType<typeof loadConfig> | undefined;
|
||||
let jiraCache: ReturnType<typeof createJiraClient> | undefined;
|
||||
let confluenceCache: ReturnType<typeof createConfluenceClient> | undefined;
|
||||
|
||||
function getConfig() {
|
||||
configCache ??= loadConfig(env, { cwd });
|
||||
@@ -42,6 +45,14 @@ function createRuntime(context: CliContext) {
|
||||
return jiraCache;
|
||||
}
|
||||
|
||||
function getConfluenceClient() {
|
||||
confluenceCache ??= createConfluenceClient({
|
||||
config: getConfig(),
|
||||
fetchImpl: context.fetchImpl,
|
||||
});
|
||||
return confluenceCache;
|
||||
}
|
||||
|
||||
async function readBodyFile(filePath: string | undefined) {
|
||||
if (!filePath) {
|
||||
return undefined;
|
||||
@@ -57,6 +68,8 @@ function createRuntime(context: CliContext) {
|
||||
readBodyFile,
|
||||
getConfig,
|
||||
getJiraClient,
|
||||
getConfluenceClient,
|
||||
fetchImpl: context.fetchImpl,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -88,6 +101,120 @@ export function buildProgram(context: CliContext = {}) {
|
||||
);
|
||||
});
|
||||
|
||||
program
|
||||
.command("conf-search")
|
||||
.requiredOption("--query <query>", "CQL search query")
|
||||
.option("--max-results <number>", "Maximum results to return", "50")
|
||||
.option("--start-at <number>", "Result offset", "0")
|
||||
.option("--format <format>", "Output format", "json")
|
||||
.action(async (options) => {
|
||||
const payload = await runtime.getConfluenceClient().searchPages({
|
||||
query: options.query,
|
||||
maxResults: Number(options.maxResults),
|
||||
startAt: Number(options.startAt),
|
||||
});
|
||||
|
||||
writeOutput(runtime.stdout, payload, resolveFormat(options.format));
|
||||
});
|
||||
|
||||
program
|
||||
.command("conf-get")
|
||||
.requiredOption("--page <page>", "Confluence page ID")
|
||||
.option("--format <format>", "Output format", "json")
|
||||
.action(async (options) => {
|
||||
const payload = await runtime.getConfluenceClient().getPage(options.page);
|
||||
writeOutput(runtime.stdout, payload, resolveFormat(options.format));
|
||||
});
|
||||
|
||||
program
|
||||
.command("conf-create")
|
||||
.requiredOption("--title <title>", "Confluence page title")
|
||||
.requiredOption("--body-file <path>", "Workspace-relative storage-format body file")
|
||||
.option("--space <space>", "Confluence space ID")
|
||||
.option("--dry-run", "Print the request without sending it")
|
||||
.option("--format <format>", "Output format", "json")
|
||||
.action(async (options) => {
|
||||
const payload = await runtime.getConfluenceClient().createPage({
|
||||
space: options.space,
|
||||
title: options.title,
|
||||
body: (await runtime.readBodyFile(options.bodyFile)) as string,
|
||||
dryRun: Boolean(options.dryRun),
|
||||
});
|
||||
|
||||
writeOutput(runtime.stdout, payload, resolveFormat(options.format));
|
||||
});
|
||||
|
||||
program
|
||||
.command("conf-update")
|
||||
.requiredOption("--page <page>", "Confluence page ID")
|
||||
.requiredOption("--title <title>", "Confluence page title")
|
||||
.requiredOption("--body-file <path>", "Workspace-relative storage-format body file")
|
||||
.option("--dry-run", "Print the request without sending it")
|
||||
.option("--format <format>", "Output format", "json")
|
||||
.action(async (options) => {
|
||||
const payload = await runtime.getConfluenceClient().updatePage({
|
||||
pageId: options.page,
|
||||
title: options.title,
|
||||
body: (await runtime.readBodyFile(options.bodyFile)) as string,
|
||||
dryRun: Boolean(options.dryRun),
|
||||
});
|
||||
|
||||
writeOutput(runtime.stdout, payload, resolveFormat(options.format));
|
||||
});
|
||||
|
||||
program
|
||||
.command("conf-comment")
|
||||
.requiredOption("--page <page>", "Confluence page ID")
|
||||
.requiredOption("--body-file <path>", "Workspace-relative storage-format body file")
|
||||
.option("--dry-run", "Print the request without sending it")
|
||||
.option("--format <format>", "Output format", "json")
|
||||
.action(async (options) => {
|
||||
const payload = await runtime.getConfluenceClient().commentPage({
|
||||
pageId: options.page,
|
||||
body: (await runtime.readBodyFile(options.bodyFile)) as string,
|
||||
dryRun: Boolean(options.dryRun),
|
||||
});
|
||||
|
||||
writeOutput(runtime.stdout, payload, resolveFormat(options.format));
|
||||
});
|
||||
|
||||
program
|
||||
.command("conf-children")
|
||||
.requiredOption("--page <page>", "Confluence page ID")
|
||||
.option("--max-results <number>", "Maximum results to return", "50")
|
||||
.option("--start-at <number>", "Cursor/start token", "0")
|
||||
.option("--format <format>", "Output format", "json")
|
||||
.action(async (options) => {
|
||||
const payload = await runtime.getConfluenceClient().listChildren(
|
||||
options.page,
|
||||
Number(options.maxResults),
|
||||
Number(options.startAt),
|
||||
);
|
||||
|
||||
writeOutput(runtime.stdout, payload, resolveFormat(options.format));
|
||||
});
|
||||
|
||||
program
|
||||
.command("raw")
|
||||
.requiredOption("--product <product>", "jira or confluence")
|
||||
.requiredOption("--method <method>", "GET, POST, or PUT")
|
||||
.requiredOption("--path <path>", "Validated API path")
|
||||
.option("--body-file <path>", "Workspace-relative JSON file")
|
||||
.option("--dry-run", "Print the request without sending it")
|
||||
.option("--format <format>", "Output format", "json")
|
||||
.action(async (options) => {
|
||||
const payload = await runRawCommand(runtime.getConfig(), runtime.fetchImpl, {
|
||||
product: options.product,
|
||||
method: String(options.method).toUpperCase(),
|
||||
path: options.path,
|
||||
bodyFile: options.bodyFile,
|
||||
cwd: runtime.cwd,
|
||||
dryRun: Boolean(options.dryRun),
|
||||
});
|
||||
|
||||
writeOutput(runtime.stdout, payload, resolveFormat(options.format));
|
||||
});
|
||||
|
||||
program
|
||||
.command("jira-search")
|
||||
.requiredOption("--jql <jql>", "JQL expression to execute")
|
||||
|
||||
Reference in New Issue
Block a user