backlog-exporter 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +593 -0
- package/bin/dev.cmd +3 -0
- package/bin/dev.js +5 -0
- package/bin/run.cmd +3 -0
- package/bin/run.js +5 -0
- package/dist/commands/all/index.d.ts +17 -0
- package/dist/commands/all/index.js +64 -0
- package/dist/commands/hello/index.d.ts +12 -0
- package/dist/commands/hello/index.js +19 -0
- package/dist/commands/hello/world.d.ts +8 -0
- package/dist/commands/hello/world.js +14 -0
- package/dist/commands/issue/index.d.ts +17 -0
- package/dist/commands/issue/index.js +53 -0
- package/dist/commands/wiki/index.d.ts +15 -0
- package/dist/commands/wiki/index.js +42 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/utils/backlog-api.d.ts +21 -0
- package/dist/utils/backlog-api.js +141 -0
- package/dist/utils/backlog.d.ts +16 -0
- package/dist/utils/backlog.js +34 -0
- package/dist/utils/common.d.ts +25 -0
- package/dist/utils/common.js +54 -0
- package/oclif.manifest.json +299 -0
- package/package.json +79 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class Hello extends Command {
|
|
3
|
+
static args: {
|
|
4
|
+
person: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
9
|
+
from: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
};
|
|
11
|
+
run(): Promise<void>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
2
|
+
export default class Hello extends Command {
|
|
3
|
+
static args = {
|
|
4
|
+
person: Args.string({ description: 'Person to say hello to', required: true }),
|
|
5
|
+
};
|
|
6
|
+
static description = 'Say hello';
|
|
7
|
+
static examples = [
|
|
8
|
+
`<%= config.bin %> <%= command.id %> friend --from oclif
|
|
9
|
+
hello friend from oclif! (./src/commands/hello/index.ts)
|
|
10
|
+
`,
|
|
11
|
+
];
|
|
12
|
+
static flags = {
|
|
13
|
+
from: Flags.string({ char: 'f', description: 'Who is saying hello', required: true }),
|
|
14
|
+
};
|
|
15
|
+
async run() {
|
|
16
|
+
const { args, flags } = await this.parse(Hello);
|
|
17
|
+
this.log(`hello ${args.person} from ${flags.from}! (./src/commands/hello/index.ts)`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class World extends Command {
|
|
3
|
+
static args = {};
|
|
4
|
+
static description = 'Say hello world';
|
|
5
|
+
static examples = [
|
|
6
|
+
`<%= config.bin %> <%= command.id %>
|
|
7
|
+
hello world! (./src/commands/hello/world.ts)
|
|
8
|
+
`,
|
|
9
|
+
];
|
|
10
|
+
static flags = {};
|
|
11
|
+
async run() {
|
|
12
|
+
this.log('hello world! (./src/commands/hello/world.ts)');
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class DownloadIssue extends Command {
|
|
3
|
+
static args: {
|
|
4
|
+
url: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
9
|
+
apiKey: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
count: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
domain: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
projectIdOrKey: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
statusId: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
};
|
|
16
|
+
run(): Promise<void>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
2
|
+
import * as dotenv from 'dotenv';
|
|
3
|
+
import { downloadIssues } from '../../utils/backlog-api.js';
|
|
4
|
+
import { validateAndGetProjectId } from '../../utils/backlog.js';
|
|
5
|
+
import { createOutputDirectory, getApiKey } from '../../utils/common.js';
|
|
6
|
+
// .envファイルを読み込む
|
|
7
|
+
dotenv.config();
|
|
8
|
+
export default class DownloadIssue extends Command {
|
|
9
|
+
static args = {
|
|
10
|
+
url: Args.string({ description: 'URL to download from', required: false }),
|
|
11
|
+
};
|
|
12
|
+
static description = 'Backlogから課題をダウンロードする';
|
|
13
|
+
static examples = [
|
|
14
|
+
`<%= config.bin %> <%= command.id %> --domain cm1.backlog.jp --projectIdOrKey PROJECT_KEY --apiKey YOUR_API_KEY --output ./issue-data
|
|
15
|
+
BacklogからAPIキーを使用して課題をダウンロードする
|
|
16
|
+
`,
|
|
17
|
+
];
|
|
18
|
+
static flags = {
|
|
19
|
+
apiKey: Flags.string({
|
|
20
|
+
description: 'Backlog API key (環境変数 BACKLOG_API_KEY からも自動読み取り可能)',
|
|
21
|
+
required: false,
|
|
22
|
+
}),
|
|
23
|
+
count: Flags.integer({ char: 'c', default: 100, description: '一度に取得する課題数', required: false }),
|
|
24
|
+
domain: Flags.string({ description: 'Backlog domain (e.g. example.backlog.jp)', required: true }),
|
|
25
|
+
output: Flags.string({
|
|
26
|
+
char: 'o',
|
|
27
|
+
default: './backlog-issues',
|
|
28
|
+
description: '出力ディレクトリパス',
|
|
29
|
+
required: false,
|
|
30
|
+
}),
|
|
31
|
+
projectIdOrKey: Flags.string({ description: 'Backlog project ID or key', required: true }),
|
|
32
|
+
statusId: Flags.string({ description: 'Filter issues by status ID', required: false }),
|
|
33
|
+
};
|
|
34
|
+
async run() {
|
|
35
|
+
const { flags } = await this.parse(DownloadIssue);
|
|
36
|
+
const { count, domain, output: outputDir, projectIdOrKey, statusId } = flags;
|
|
37
|
+
const apiKey = getApiKey(this, flags.apiKey);
|
|
38
|
+
this.log(`Backlogから ${domain} のプロジェクト ${projectIdOrKey} の課題を取得しています...`);
|
|
39
|
+
try {
|
|
40
|
+
// プロジェクトキーからプロジェクトIDを取得
|
|
41
|
+
const projectId = await validateAndGetProjectId(domain, projectIdOrKey, apiKey);
|
|
42
|
+
this.log(`プロジェクトID: ${projectId} を使用します`);
|
|
43
|
+
// 出力ディレクトリの作成
|
|
44
|
+
await createOutputDirectory(outputDir);
|
|
45
|
+
// 課題のダウンロード
|
|
46
|
+
await downloadIssues(this, domain, projectId, apiKey, outputDir, count, statusId);
|
|
47
|
+
this.log('ダウンロードが完了しました!');
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
this.error(`ダウンロードに失敗しました: ${error instanceof Error ? error.message : String(error)}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class DownloadWiki extends Command {
|
|
3
|
+
static args: {
|
|
4
|
+
url: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
9
|
+
apiKey: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
domain: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
projectIdOrKey: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
};
|
|
14
|
+
run(): Promise<void>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
2
|
+
import * as dotenv from 'dotenv';
|
|
3
|
+
import { downloadWikis } from '../../utils/backlog-api.js';
|
|
4
|
+
import { createOutputDirectory, getApiKey } from '../../utils/common.js';
|
|
5
|
+
// .envファイルを読み込む
|
|
6
|
+
dotenv.config();
|
|
7
|
+
export default class DownloadWiki extends Command {
|
|
8
|
+
static args = {
|
|
9
|
+
url: Args.string({ description: 'URL to download from', required: false }),
|
|
10
|
+
};
|
|
11
|
+
static description = 'BacklogからWikiコンテンツをダウンロードする';
|
|
12
|
+
static examples = [
|
|
13
|
+
`<%= config.bin %> <%= command.id %> --domain cm1.backlog.jp --projectIdOrKey PROJECT_KEY --apiKey YOUR_API_KEY --output ./wiki-data
|
|
14
|
+
BacklogからAPIキーを使用してWikiコンテンツをダウンロードする
|
|
15
|
+
`,
|
|
16
|
+
];
|
|
17
|
+
static flags = {
|
|
18
|
+
apiKey: Flags.string({
|
|
19
|
+
description: 'Backlog API key (環境変数 BACKLOG_API_KEY からも自動読み取り可能)',
|
|
20
|
+
required: false,
|
|
21
|
+
}),
|
|
22
|
+
domain: Flags.string({ description: 'Backlog domain (e.g. example.backlog.jp)', required: true }),
|
|
23
|
+
output: Flags.string({ char: 'o', default: './backlog-wiki', description: '出力ディレクトリパス', required: false }),
|
|
24
|
+
projectIdOrKey: Flags.string({ description: 'Backlog project ID or key', required: true }),
|
|
25
|
+
};
|
|
26
|
+
async run() {
|
|
27
|
+
const { flags } = await this.parse(DownloadWiki);
|
|
28
|
+
const { domain, output: outputDir, projectIdOrKey } = flags;
|
|
29
|
+
const apiKey = getApiKey(this, flags.apiKey);
|
|
30
|
+
this.log(`Backlogから ${domain} のプロジェクト ${projectIdOrKey} のWikiを取得しています...`);
|
|
31
|
+
try {
|
|
32
|
+
// 出力ディレクトリの作成
|
|
33
|
+
await createOutputDirectory(outputDir);
|
|
34
|
+
// Wikiのダウンロード
|
|
35
|
+
await downloadWikis(this, domain, projectIdOrKey, apiKey, outputDir);
|
|
36
|
+
this.log('ダウンロードが完了しました!');
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
this.error(`ダウンロードに失敗しました: ${error instanceof Error ? error.message : String(error)}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { run } from '@oclif/core';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { run } from '@oclif/core';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
/**
|
|
3
|
+
* Backlogから課題をダウンロードする
|
|
4
|
+
* @param command コマンドインスタンス
|
|
5
|
+
* @param domain Backlogドメイン
|
|
6
|
+
* @param projectId プロジェクトID
|
|
7
|
+
* @param apiKey APIキー
|
|
8
|
+
* @param outputDir 出力ディレクトリ
|
|
9
|
+
* @param count 一度に取得する課題数
|
|
10
|
+
* @param statusId ステータスID(オプション)
|
|
11
|
+
*/
|
|
12
|
+
export declare function downloadIssues(command: Command, domain: string, projectId: number, apiKey: string, outputDir: string, count: number, statusId?: string): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* BacklogからWikiをダウンロードする
|
|
15
|
+
* @param command コマンドインスタンス
|
|
16
|
+
* @param domain Backlogドメイン
|
|
17
|
+
* @param projectIdOrKey プロジェクトIDまたはキー
|
|
18
|
+
* @param apiKey APIキー
|
|
19
|
+
* @param outputDir 出力ディレクトリ
|
|
20
|
+
*/
|
|
21
|
+
export declare function downloadWikis(command: Command, domain: string, projectIdOrKey: string, apiKey: string, outputDir: string): Promise<void>;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import ky from 'ky';
|
|
2
|
+
import * as fs from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { sanitizeFileName, sanitizeWikiFileName } from './common.js';
|
|
5
|
+
/**
|
|
6
|
+
* Backlogから課題をダウンロードする
|
|
7
|
+
* @param command コマンドインスタンス
|
|
8
|
+
* @param domain Backlogドメイン
|
|
9
|
+
* @param projectId プロジェクトID
|
|
10
|
+
* @param apiKey APIキー
|
|
11
|
+
* @param outputDir 出力ディレクトリ
|
|
12
|
+
* @param count 一度に取得する課題数
|
|
13
|
+
* @param statusId ステータスID(オプション)
|
|
14
|
+
*/
|
|
15
|
+
export async function downloadIssues(command, domain, projectId, apiKey, outputDir, count, statusId) {
|
|
16
|
+
const baseUrl = `https://${domain}/api/v2`;
|
|
17
|
+
command.log('課題の取得を開始します...');
|
|
18
|
+
// APIパラメータの構築
|
|
19
|
+
const params = new URLSearchParams({
|
|
20
|
+
apiKey,
|
|
21
|
+
count: count.toString(),
|
|
22
|
+
'projectId[]': projectId.toString(),
|
|
23
|
+
});
|
|
24
|
+
// ステータスIDが指定されている場合は追加
|
|
25
|
+
if (statusId) {
|
|
26
|
+
params.append('statusId[]', statusId);
|
|
27
|
+
}
|
|
28
|
+
const issues = await ky.get(`${baseUrl}/issues?${params.toString()}`).json();
|
|
29
|
+
command.log(`${issues.length}件の課題が見つかりました。`);
|
|
30
|
+
// 各課題の詳細情報を取得して保存
|
|
31
|
+
command.log('課題を保存しています...');
|
|
32
|
+
// Promise.allを使用して並列処理
|
|
33
|
+
const issuePromises = issues.map(async (issue) => {
|
|
34
|
+
try {
|
|
35
|
+
// 課題の詳細情報をJSONファイルとして保存
|
|
36
|
+
const issueFileName = `${sanitizeFileName(issue.summary)}.md`;
|
|
37
|
+
const issueFilePath = path.join(outputDir, issueFileName);
|
|
38
|
+
// BacklogのIssueへのリンクを作成
|
|
39
|
+
const backlogIssueUrl = `https://${domain}/view/${issue.issueKey}`;
|
|
40
|
+
// コメント一覧を取得
|
|
41
|
+
command.log(`課題 ${issue.issueKey} のコメントを取得しています...`);
|
|
42
|
+
const commentsParams = new URLSearchParams({
|
|
43
|
+
apiKey,
|
|
44
|
+
count: '100', // 最大100件のコメントを取得
|
|
45
|
+
order: 'asc', // 古い順に取得
|
|
46
|
+
});
|
|
47
|
+
const comments = await ky.get(`${baseUrl}/issues/${issue.issueKey}/comments?${commentsParams.toString()}`).json();
|
|
48
|
+
// コメントセクションを作成
|
|
49
|
+
let commentsSection = '';
|
|
50
|
+
if (comments.length > 0) {
|
|
51
|
+
commentsSection = '\n\n## コメント\n';
|
|
52
|
+
let commentIndex = 1;
|
|
53
|
+
for (const comment of comments) {
|
|
54
|
+
const commentDate = new Date(comment.created).toLocaleString('ja-JP');
|
|
55
|
+
commentsSection += `\n### コメント ${commentIndex}\n- **投稿者**: ${comment.createdUser.name}\n- **日時**: ${commentDate}\n\n${comment.content || '(内容なし)'}\n\n---\n`;
|
|
56
|
+
commentIndex++;
|
|
57
|
+
}
|
|
58
|
+
// 最後の区切り線を削除
|
|
59
|
+
commentsSection = commentsSection.slice(0, -5);
|
|
60
|
+
}
|
|
61
|
+
// Markdownファイルに書き込む
|
|
62
|
+
const assigneeName = issue.assignee ? issue.assignee.name : '未割り当て';
|
|
63
|
+
const markdownContent = `# ${issue.summary}
|
|
64
|
+
|
|
65
|
+
## 基本情報
|
|
66
|
+
- 課題キー: ${issue.issueKey}
|
|
67
|
+
- ステータス: ${issue.status.name}
|
|
68
|
+
- 優先度: ${issue.priority.name}
|
|
69
|
+
- 担当者: ${assigneeName}
|
|
70
|
+
- 作成日時: ${new Date(issue.created).toLocaleString('ja-JP')}
|
|
71
|
+
- 更新日時: ${new Date(issue.updated).toLocaleString('ja-JP')}
|
|
72
|
+
- [Backlog Issue Link](${backlogIssueUrl})
|
|
73
|
+
|
|
74
|
+
## 詳細
|
|
75
|
+
${issue.description || '詳細情報なし'}${commentsSection}`;
|
|
76
|
+
await fs.writeFile(issueFilePath, markdownContent);
|
|
77
|
+
command.log(`課題 "${issue.issueKey}: ${issue.summary}" を ${issueFilePath} に保存しました`);
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
command.warn(`課題 ${issue.issueKey} の保存に失敗しました: ${error instanceof Error ? error.message : String(error)}`);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
await Promise.all(issuePromises);
|
|
84
|
+
command.log('課題のダウンロードが完了しました!');
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* BacklogからWikiをダウンロードする
|
|
88
|
+
* @param command コマンドインスタンス
|
|
89
|
+
* @param domain Backlogドメイン
|
|
90
|
+
* @param projectIdOrKey プロジェクトIDまたはキー
|
|
91
|
+
* @param apiKey APIキー
|
|
92
|
+
* @param outputDir 出力ディレクトリ
|
|
93
|
+
*/
|
|
94
|
+
export async function downloadWikis(command, domain, projectIdOrKey, apiKey, outputDir) {
|
|
95
|
+
const baseUrl = `https://${domain}/api/v2`;
|
|
96
|
+
command.log('Wikiの取得を開始します...');
|
|
97
|
+
// Wiki一覧の取得
|
|
98
|
+
command.log('Wiki一覧を取得しています...');
|
|
99
|
+
const wikis = await ky
|
|
100
|
+
.get(`${baseUrl}/wikis?apiKey=${apiKey}&projectIdOrKey=${projectIdOrKey}`)
|
|
101
|
+
.json();
|
|
102
|
+
command.log(`${wikis.length}件のWikiが見つかりました。`);
|
|
103
|
+
// 各Wikiの詳細情報を取得
|
|
104
|
+
command.log('Wiki詳細を取得しています...');
|
|
105
|
+
// Promise.allを使用して並列処理
|
|
106
|
+
const wikiPromises = wikis.map(async (wiki) => {
|
|
107
|
+
const wikiId = wiki.id;
|
|
108
|
+
command.log(`Wiki: ${wiki.name} (ID: ${wikiId}) を取得しています`);
|
|
109
|
+
try {
|
|
110
|
+
const wikiDetail = await ky
|
|
111
|
+
.get(`${baseUrl}/wikis/${wikiId}?projectIdOrKey=${projectIdOrKey}&apiKey=${apiKey}`)
|
|
112
|
+
.json();
|
|
113
|
+
// Wikiの名前をファイルパスとして使用
|
|
114
|
+
const wikiName = wiki.name;
|
|
115
|
+
// ファイル名のサニタイズ
|
|
116
|
+
const sanitizedName = sanitizeWikiFileName(wikiName);
|
|
117
|
+
// ファイル名の拡張子を追加
|
|
118
|
+
const wikiFileName = `${sanitizedName}.md`;
|
|
119
|
+
// ディレクトリ構造を作成(必要な場合)
|
|
120
|
+
const dirPath = path.dirname(wikiFileName);
|
|
121
|
+
if (dirPath !== '.') {
|
|
122
|
+
await fs.mkdir(path.join(outputDir, dirPath), { recursive: true });
|
|
123
|
+
}
|
|
124
|
+
// Markdownファイルを保存
|
|
125
|
+
const wikiFilePath = path.join(outputDir, wikiFileName);
|
|
126
|
+
// JSONからcontentフィールドを取得
|
|
127
|
+
const content = wikiDetail.content || '';
|
|
128
|
+
// BacklogのWikiへのリンクを作成
|
|
129
|
+
const backlogWikiUrl = `https://${domain}/alias/wiki/${wikiId}`;
|
|
130
|
+
// Markdownファイルに書き込む(タイトルとBacklogリンクを追加)
|
|
131
|
+
const markdownContent = `# ${wiki.name}\n\n[Backlog Wiki Link](${backlogWikiUrl})\n\n${content}`;
|
|
132
|
+
await fs.writeFile(wikiFilePath, markdownContent);
|
|
133
|
+
command.log(`Wiki "${wiki.name}" を ${wikiFilePath} に保存しました`);
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
command.warn(`Wiki ID ${wikiId} の取得に失敗しました: ${error instanceof Error ? error.message : String(error)}`);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
await Promise.all(wikiPromises);
|
|
140
|
+
command.log('Wikiのダウンロードが完了しました!');
|
|
141
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* プロジェクトキーからプロジェクトIDを取得する
|
|
3
|
+
* @param domain Backlogドメイン (例: example.backlog.jp)
|
|
4
|
+
* @param projectKey プロジェクトキー
|
|
5
|
+
* @param apiKey BacklogのAPIキー
|
|
6
|
+
* @returns プロジェクトID
|
|
7
|
+
*/
|
|
8
|
+
export declare function getProjectIdFromKey(domain: string, projectKey: string, apiKey: string): Promise<number>;
|
|
9
|
+
/**
|
|
10
|
+
* プロジェクトIDまたはキーを検証し、必要に応じてキーからIDを取得する
|
|
11
|
+
* @param domain Backlogドメイン
|
|
12
|
+
* @param projectIdOrKey プロジェクトIDまたはキー
|
|
13
|
+
* @param apiKey BacklogのAPIキー
|
|
14
|
+
* @returns プロジェクトID
|
|
15
|
+
*/
|
|
16
|
+
export declare function validateAndGetProjectId(domain: string, projectIdOrKey: string, apiKey: string): Promise<number>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import ky from 'ky';
|
|
2
|
+
/**
|
|
3
|
+
* プロジェクトキーからプロジェクトIDを取得する
|
|
4
|
+
* @param domain Backlogドメイン (例: example.backlog.jp)
|
|
5
|
+
* @param projectKey プロジェクトキー
|
|
6
|
+
* @param apiKey BacklogのAPIキー
|
|
7
|
+
* @returns プロジェクトID
|
|
8
|
+
*/
|
|
9
|
+
export async function getProjectIdFromKey(domain, projectKey, apiKey) {
|
|
10
|
+
try {
|
|
11
|
+
const baseUrl = `https://${domain}/api/v2`;
|
|
12
|
+
const projectUrl = `${baseUrl}/projects/${projectKey}?apiKey=${apiKey}`;
|
|
13
|
+
const projectData = await ky.get(projectUrl).json();
|
|
14
|
+
return projectData.id;
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
throw new Error(`プロジェクトキー "${projectKey}" からプロジェクトIDの取得に失敗しました: ${error instanceof Error ? error.message : String(error)}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* プロジェクトIDまたはキーを検証し、必要に応じてキーからIDを取得する
|
|
22
|
+
* @param domain Backlogドメイン
|
|
23
|
+
* @param projectIdOrKey プロジェクトIDまたはキー
|
|
24
|
+
* @param apiKey BacklogのAPIキー
|
|
25
|
+
* @returns プロジェクトID
|
|
26
|
+
*/
|
|
27
|
+
export async function validateAndGetProjectId(domain, projectIdOrKey, apiKey) {
|
|
28
|
+
// 数値の場合はそのままプロジェクトIDとして返す
|
|
29
|
+
if (!Number.isNaN(Number(projectIdOrKey))) {
|
|
30
|
+
return Number(projectIdOrKey);
|
|
31
|
+
}
|
|
32
|
+
// 文字列の場合はプロジェクトキーとして扱い、IDを取得する
|
|
33
|
+
return getProjectIdFromKey(domain, projectIdOrKey, apiKey);
|
|
34
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
/**
|
|
3
|
+
* APIキーを取得する(優先順位: コマンドライン引数 > 環境変数)
|
|
4
|
+
* @param command コマンドインスタンス
|
|
5
|
+
* @param providedApiKey コマンドライン引数から提供されたAPIキー
|
|
6
|
+
* @returns APIキー
|
|
7
|
+
*/
|
|
8
|
+
export declare function getApiKey(command: Command, providedApiKey?: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* ファイル名に使用できない文字を置換する関数
|
|
11
|
+
* @param name 元のファイル名
|
|
12
|
+
* @returns サニタイズされたファイル名
|
|
13
|
+
*/
|
|
14
|
+
export declare function sanitizeFileName(name: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* Wikiファイル名のサニタイズ(スラッシュはディレクトリ区切りとして使用するため残す)
|
|
17
|
+
* @param name 元のWiki名
|
|
18
|
+
* @returns サニタイズされたWikiファイル名
|
|
19
|
+
*/
|
|
20
|
+
export declare function sanitizeWikiFileName(name: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* 出力ディレクトリを作成する
|
|
23
|
+
* @param outputDir 出力ディレクトリパス
|
|
24
|
+
*/
|
|
25
|
+
export declare function createOutputDirectory(outputDir: string): Promise<void>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import * as fs from 'node:fs/promises';
|
|
2
|
+
/**
|
|
3
|
+
* APIキーを取得する(優先順位: コマンドライン引数 > 環境変数)
|
|
4
|
+
* @param command コマンドインスタンス
|
|
5
|
+
* @param providedApiKey コマンドライン引数から提供されたAPIキー
|
|
6
|
+
* @returns APIキー
|
|
7
|
+
*/
|
|
8
|
+
export function getApiKey(command, providedApiKey) {
|
|
9
|
+
// コマンドライン引数からのAPIキー
|
|
10
|
+
if (providedApiKey) {
|
|
11
|
+
return providedApiKey;
|
|
12
|
+
}
|
|
13
|
+
// 環境変数からのAPIキー
|
|
14
|
+
const envApiKey = process.env.BACKLOG_API_KEY;
|
|
15
|
+
if (envApiKey) {
|
|
16
|
+
command.log('環境変数 BACKLOG_API_KEY からAPIキーを使用します');
|
|
17
|
+
return envApiKey;
|
|
18
|
+
}
|
|
19
|
+
// APIキーが見つからない場合はエラー
|
|
20
|
+
command.error('APIキーが見つかりません。--apiKey フラグまたは BACKLOG_API_KEY 環境変数で提供してください');
|
|
21
|
+
return ''; // この行は実行されないが、TypeScriptのエラーを回避するために必要
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* ファイル名に使用できない文字を置換する関数
|
|
25
|
+
* @param name 元のファイル名
|
|
26
|
+
* @returns サニタイズされたファイル名
|
|
27
|
+
*/
|
|
28
|
+
export function sanitizeFileName(name) {
|
|
29
|
+
return name
|
|
30
|
+
.replaceAll(/[\\/:*?"<>|]/g, '_') // Windowsで使用できない文字を置換
|
|
31
|
+
.replaceAll(/\s+/g, '_') // スペースをアンダースコアに置換
|
|
32
|
+
.replaceAll('.', '_') // ドットを置換
|
|
33
|
+
.slice(0, 200); // 長すぎるファイル名を防ぐために200文字に制限
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Wikiファイル名のサニタイズ(スラッシュはディレクトリ区切りとして使用するため残す)
|
|
37
|
+
* @param name 元のWiki名
|
|
38
|
+
* @returns サニタイズされたWikiファイル名
|
|
39
|
+
*/
|
|
40
|
+
export function sanitizeWikiFileName(name) {
|
|
41
|
+
const invalidChars = ['\\', ':', '*', '?', '"', '<', '>', '|'];
|
|
42
|
+
let sanitizedName = name;
|
|
43
|
+
for (const char of invalidChars) {
|
|
44
|
+
sanitizedName = sanitizedName.replaceAll(char, '_');
|
|
45
|
+
}
|
|
46
|
+
return sanitizedName;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 出力ディレクトリを作成する
|
|
50
|
+
* @param outputDir 出力ディレクトリパス
|
|
51
|
+
*/
|
|
52
|
+
export async function createOutputDirectory(outputDir) {
|
|
53
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
54
|
+
}
|