backlog-exporter 0.1.0 → 0.2.0

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 CHANGED
@@ -59,7 +59,7 @@ $ npm install -g backlog-exporter
59
59
  $ backlog-exporter COMMAND
60
60
  running command...
61
61
  $ backlog-exporter (--version)
62
- backlog-exporter/0.1.0 linux-x64 node-v23.9.0
62
+ backlog-exporter/0.2.0 linux-x64 node-v23.9.0
63
63
  $ backlog-exporter --help [COMMAND]
64
64
  USAGE
65
65
  $ backlog-exporter COMMAND
@@ -215,7 +215,7 @@ EXAMPLES
215
215
  指定したディレクトリに課題とWikiを保存する
216
216
  ```
217
217
 
218
- _See code: [src/commands/all/index.ts](https://github.com/ShuntaToda/backlog-exporter/blob/v0.1.0/src/commands/all/index.ts)_
218
+ _See code: [src/commands/all/index.ts](https://github.com/ShuntaToda/backlog-exporter/blob/v0.2.0/src/commands/all/index.ts)_
219
219
 
220
220
  ## `backlog-exporter help [COMMAND]`
221
221
 
@@ -262,7 +262,7 @@ EXAMPLES
262
262
  指定したディレクトリに課題を保存する
263
263
  ```
264
264
 
265
- _See code: [src/commands/issue/index.ts](https://github.com/ShuntaToda/backlog-exporter/blob/v0.1.0/src/commands/issue/index.ts)_
265
+ _See code: [src/commands/issue/index.ts](https://github.com/ShuntaToda/backlog-exporter/blob/v0.2.0/src/commands/issue/index.ts)_
266
266
 
267
267
  ## `backlog-exporter plugins`
268
268
 
@@ -591,7 +591,7 @@ EXAMPLES
591
591
  指定したディレクトリの設定を使用して更新する
592
592
  ```
593
593
 
594
- _See code: [src/commands/update/index.ts](https://github.com/ShuntaToda/backlog-exporter/blob/v0.1.0/src/commands/update/index.ts)_
594
+ _See code: [src/commands/update/index.ts](https://github.com/ShuntaToda/backlog-exporter/blob/v0.2.0/src/commands/update/index.ts)_
595
595
 
596
596
  ## `backlog-exporter wiki`
597
597
 
@@ -618,7 +618,7 @@ EXAMPLES
618
618
  指定したディレクトリにWikiを保存する
619
619
  ```
620
620
 
621
- _See code: [src/commands/wiki/index.ts](https://github.com/ShuntaToda/backlog-exporter/blob/v0.1.0/src/commands/wiki/index.ts)_
621
+ _See code: [src/commands/wiki/index.ts](https://github.com/ShuntaToda/backlog-exporter/blob/v0.2.0/src/commands/wiki/index.ts)_
622
622
  <!-- commandsstop -->
623
623
 
624
624
  # 出力形式
@@ -55,7 +55,13 @@ export default class All extends Command {
55
55
  await createOutputDirectory(wikiOutput);
56
56
  // 課題の取得と保存
57
57
  this.log('課題の取得を開始します...');
58
- await downloadIssues(this, domain, projectId, apiKey, issueOutput, 100);
58
+ await downloadIssues(this, {
59
+ apiKey,
60
+ count: 100,
61
+ domain,
62
+ outputDir: issueOutput,
63
+ projectId,
64
+ });
59
65
  // 課題フォルダに設定ファイルを保存
60
66
  await updateSettings(issueOutput, {
61
67
  apiKey,
@@ -68,7 +74,12 @@ export default class All extends Command {
68
74
  this.log('課題の取得が完了しました');
69
75
  // Wikiの取得と保存
70
76
  this.log('Wikiの取得を開始します...');
71
- await downloadWikis(this, domain, projectIdOrKey, apiKey, wikiOutput);
77
+ await downloadWikis(this, {
78
+ apiKey,
79
+ domain,
80
+ outputDir: wikiOutput,
81
+ projectIdOrKey,
82
+ });
72
83
  // Wikiフォルダに設定ファイルを保存
73
84
  await updateSettings(wikiOutput, {
74
85
  apiKey,
@@ -47,7 +47,13 @@ export default class Issue extends Command {
47
47
  const projectId = await validateAndGetProjectId(domain, projectIdOrKey, apiKey);
48
48
  this.log(`プロジェクトID: ${projectId} を使用します`);
49
49
  // 課題の取得と保存
50
- await downloadIssues(this, domain, projectId, apiKey, outputDir, 100);
50
+ await downloadIssues(this, {
51
+ apiKey,
52
+ count: 100,
53
+ domain,
54
+ outputDir,
55
+ projectId,
56
+ });
51
57
  // 設定ファイルを保存
52
58
  await updateSettings(outputDir, {
53
59
  apiKey,
@@ -207,7 +207,16 @@ export default class Update extends Command {
207
207
  // 課題の更新
208
208
  async updateIssues(options) {
209
209
  this.log('課題の更新を開始します...');
210
- await downloadIssues(this, options.domain, options.projectId, options.apiKey, options.targetDir, 100);
210
+ // 設定ファイルから前回の更新日時を取得
211
+ const { lastUpdated } = await loadSettings(options.targetDir);
212
+ await downloadIssues(this, {
213
+ apiKey: options.apiKey,
214
+ count: 100,
215
+ domain: options.domain,
216
+ lastUpdated,
217
+ outputDir: options.targetDir,
218
+ projectId: options.projectId,
219
+ });
211
220
  // 設定ファイルを更新
212
221
  await updateSettings(options.targetDir, {
213
222
  apiKey: options.apiKey,
@@ -222,7 +231,15 @@ export default class Update extends Command {
222
231
  // Wikiの更新
223
232
  async updateWikis(options) {
224
233
  this.log('Wikiの更新を開始します...');
225
- await downloadWikis(this, options.domain, options.projectIdOrKey, options.apiKey, options.targetDir);
234
+ // 設定ファイルから前回の更新日時を取得
235
+ const { lastUpdated } = await loadSettings(options.targetDir);
236
+ await downloadWikis(this, {
237
+ apiKey: options.apiKey,
238
+ domain: options.domain,
239
+ lastUpdated,
240
+ outputDir: options.targetDir,
241
+ projectIdOrKey: options.projectIdOrKey,
242
+ });
226
243
  // 設定ファイルを更新
227
244
  await updateSettings(options.targetDir, {
228
245
  apiKey: options.apiKey,
@@ -43,7 +43,12 @@ Wikiをダウンロードする
43
43
  // 出力ディレクトリの作成
44
44
  await createOutputDirectory(outputDir);
45
45
  // Wikiの取得と保存
46
- await downloadWikis(this, domain, projectIdOrKey, apiKey, outputDir);
46
+ await downloadWikis(this, {
47
+ apiKey,
48
+ domain,
49
+ outputDir,
50
+ projectIdOrKey,
51
+ });
47
52
  // 設定ファイルを保存
48
53
  await updateSettings(outputDir, {
49
54
  apiKey,
@@ -2,20 +2,26 @@ import { Command } from '@oclif/core';
2
2
  /**
3
3
  * Backlogから課題をダウンロードする
4
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(オプション)
5
+ * @param options 課題ダウンロードのオプション
11
6
  */
12
- export declare function downloadIssues(command: Command, domain: string, projectId: number, apiKey: string, outputDir: string, count: number, statusId?: string): Promise<void>;
7
+ export declare function downloadIssues(command: Command, options: {
8
+ apiKey: string;
9
+ count: number;
10
+ domain: string;
11
+ lastUpdated?: string;
12
+ outputDir: string;
13
+ projectId: number;
14
+ statusId?: string;
15
+ }): Promise<void>;
13
16
  /**
14
17
  * BacklogからWikiをダウンロードする
15
18
  * @param command コマンドインスタンス
16
- * @param domain Backlogドメイン
17
- * @param projectIdOrKey プロジェクトIDまたはキー
18
- * @param apiKey APIキー
19
- * @param outputDir 出力ディレクトリ
19
+ * @param options Wikiダウンロードのオプション
20
20
  */
21
- export declare function downloadWikis(command: Command, domain: string, projectIdOrKey: string, apiKey: string, outputDir: string): Promise<void>;
21
+ export declare function downloadWikis(command: Command, options: {
22
+ apiKey: string;
23
+ domain: string;
24
+ lastUpdated?: string;
25
+ outputDir: string;
26
+ projectIdOrKey: string;
27
+ }): Promise<void>;
@@ -5,42 +5,51 @@ import { sanitizeFileName, sanitizeWikiFileName } from './common.js';
5
5
  /**
6
6
  * Backlogから課題をダウンロードする
7
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(オプション)
8
+ * @param options 課題ダウンロードのオプション
14
9
  */
15
- export async function downloadIssues(command, domain, projectId, apiKey, outputDir, count, statusId) {
16
- const baseUrl = `https://${domain}/api/v2`;
10
+ export async function downloadIssues(command, options) {
11
+ const baseUrl = `https://${options.domain}/api/v2`;
17
12
  command.log('課題の取得を開始します...');
18
13
  // APIパラメータの構築
19
14
  const params = new URLSearchParams({
20
- apiKey,
21
- count: count.toString(),
22
- 'projectId[]': projectId.toString(),
15
+ apiKey: options.apiKey,
16
+ count: options.count.toString(),
17
+ 'projectId[]': options.projectId.toString(),
23
18
  });
24
19
  // ステータスIDが指定されている場合は追加
25
- if (statusId) {
26
- params.append('statusId[]', statusId);
20
+ if (options.statusId) {
21
+ params.append('statusId[]', options.statusId);
27
22
  }
28
23
  const issues = await ky.get(`${baseUrl}/issues?${params.toString()}`).json();
29
24
  command.log(`${issues.length}件の課題が見つかりました。`);
25
+ // 前回の更新日時より新しい課題のみをフィルタリング
26
+ let filteredIssues = issues;
27
+ if (options.lastUpdated) {
28
+ const lastUpdatedDate = new Date(options.lastUpdated);
29
+ filteredIssues = issues.filter((issue) => {
30
+ const issueUpdatedDate = new Date(issue.updated);
31
+ return issueUpdatedDate > lastUpdatedDate;
32
+ });
33
+ command.log(`前回の更新日時(${options.lastUpdated})以降に更新された${filteredIssues.length}件の課題を処理します。`);
34
+ }
35
+ if (filteredIssues.length === 0) {
36
+ command.log('更新が必要な課題はありません。');
37
+ return;
38
+ }
30
39
  // 各課題の詳細情報を取得して保存
31
40
  command.log('課題を保存しています...');
32
41
  // Promise.allを使用して並列処理
33
- const issuePromises = issues.map(async (issue) => {
42
+ const issuePromises = filteredIssues.map(async (issue) => {
34
43
  try {
35
44
  // 課題の詳細情報をJSONファイルとして保存
36
45
  const issueFileName = `${sanitizeFileName(issue.summary)}.md`;
37
- const issueFilePath = path.join(outputDir, issueFileName);
46
+ const issueFilePath = path.join(options.outputDir, issueFileName);
38
47
  // BacklogのIssueへのリンクを作成
39
- const backlogIssueUrl = `https://${domain}/view/${issue.issueKey}`;
48
+ const backlogIssueUrl = `https://${options.domain}/view/${issue.issueKey}`;
40
49
  // コメント一覧を取得
41
50
  command.log(`課題 ${issue.issueKey} のコメントを取得しています...`);
42
51
  const commentsParams = new URLSearchParams({
43
- apiKey,
52
+ apiKey: options.apiKey,
44
53
  count: '100', // 最大100件のコメントを取得
45
54
  order: 'asc', // 古い順に取得
46
55
  });
@@ -86,29 +95,40 @@ ${issue.description || '詳細情報なし'}${commentsSection}`;
86
95
  /**
87
96
  * BacklogからWikiをダウンロードする
88
97
  * @param command コマンドインスタンス
89
- * @param domain Backlogドメイン
90
- * @param projectIdOrKey プロジェクトIDまたはキー
91
- * @param apiKey APIキー
92
- * @param outputDir 出力ディレクトリ
98
+ * @param options Wikiダウンロードのオプション
93
99
  */
94
- export async function downloadWikis(command, domain, projectIdOrKey, apiKey, outputDir) {
95
- const baseUrl = `https://${domain}/api/v2`;
100
+ export async function downloadWikis(command, options) {
101
+ const baseUrl = `https://${options.domain}/api/v2`;
96
102
  command.log('Wikiの取得を開始します...');
97
103
  // Wiki一覧の取得
98
104
  command.log('Wiki一覧を取得しています...');
99
105
  const wikis = await ky
100
- .get(`${baseUrl}/wikis?apiKey=${apiKey}&projectIdOrKey=${projectIdOrKey}`)
106
+ .get(`${baseUrl}/wikis?apiKey=${options.apiKey}&projectIdOrKey=${options.projectIdOrKey}`)
101
107
  .json();
102
108
  command.log(`${wikis.length}件のWikiが見つかりました。`);
109
+ // 前回の更新日時より新しいWikiのみをフィルタリング
110
+ let filteredWikis = wikis;
111
+ if (options.lastUpdated) {
112
+ const lastUpdatedDate = new Date(options.lastUpdated);
113
+ filteredWikis = wikis.filter((wiki) => {
114
+ const wikiUpdatedDate = new Date(wiki.updated);
115
+ return wikiUpdatedDate > lastUpdatedDate;
116
+ });
117
+ command.log(`前回の更新日時(${options.lastUpdated})以降に更新された${filteredWikis.length}件のWikiを処理します。`);
118
+ }
119
+ if (filteredWikis.length === 0) {
120
+ command.log('更新が必要なWikiはありません。');
121
+ return;
122
+ }
103
123
  // 各Wikiの詳細情報を取得
104
124
  command.log('Wiki詳細を取得しています...');
105
125
  // Promise.allを使用して並列処理
106
- const wikiPromises = wikis.map(async (wiki) => {
126
+ const wikiPromises = filteredWikis.map(async (wiki) => {
107
127
  const wikiId = wiki.id;
108
128
  command.log(`Wiki: ${wiki.name} (ID: ${wikiId}) を取得しています`);
109
129
  try {
110
130
  const wikiDetail = await ky
111
- .get(`${baseUrl}/wikis/${wikiId}?projectIdOrKey=${projectIdOrKey}&apiKey=${apiKey}`)
131
+ .get(`${baseUrl}/wikis/${wikiId}?projectIdOrKey=${options.projectIdOrKey}&apiKey=${options.apiKey}`)
112
132
  .json();
113
133
  // Wikiの名前をファイルパスとして使用
114
134
  const wikiName = wiki.name;
@@ -119,14 +139,14 @@ export async function downloadWikis(command, domain, projectIdOrKey, apiKey, out
119
139
  // ディレクトリ構造を作成(必要な場合)
120
140
  const dirPath = path.dirname(wikiFileName);
121
141
  if (dirPath !== '.') {
122
- await fs.mkdir(path.join(outputDir, dirPath), { recursive: true });
142
+ await fs.mkdir(path.join(options.outputDir, dirPath), { recursive: true });
123
143
  }
124
144
  // Markdownファイルを保存
125
- const wikiFilePath = path.join(outputDir, wikiFileName);
145
+ const wikiFilePath = path.join(options.outputDir, wikiFileName);
126
146
  // JSONからcontentフィールドを取得
127
147
  const content = wikiDetail.content || '';
128
148
  // BacklogのWikiへのリンクを作成
129
- const backlogWikiUrl = `https://${domain}/alias/wiki/${wikiId}`;
149
+ const backlogWikiUrl = `https://${options.domain}/alias/wiki/${wikiId}`;
130
150
  // Markdownファイルに書き込む(タイトルとBacklogリンクを追加)
131
151
  const markdownContent = `# ${wiki.name}\n\n[Backlog Wiki Link](${backlogWikiUrl})\n\n${content}`;
132
152
  await fs.writeFile(wikiFilePath, markdownContent);
@@ -258,5 +258,5 @@
258
258
  ]
259
259
  }
260
260
  },
261
- "version": "0.1.0"
261
+ "version": "0.2.0"
262
262
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "backlog-exporter",
3
3
  "description": "A new CLI generated with oclif",
4
- "version": "0.1.0",
4
+ "version": "0.2.0",
5
5
  "author": "ShuntaToda",
6
6
  "bin": {
7
7
  "backlog-exporter": "bin/run.js"
@@ -76,4 +76,4 @@
76
76
  "version": "oclif readme && git add README.md"
77
77
  },
78
78
  "types": "dist/index.d.ts"
79
- }
79
+ }