@d-zero/backlog-projects 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 D-ZERO Co., Ltd.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # `@d-zero/packbacklog-projects`
2
+
3
+ ## 使い方
4
+
5
+ ### プロジェクトアサイン
6
+
7
+ ```sh
8
+ npx @d-zero/backlog-projects --assign
9
+ ```
10
+
11
+ プロンプトに従って入力してください。
12
+
13
+ ```sh
14
+ ? BacklogのプロジェクトURLを入力してください ›
15
+ ? カテゴリーを入力してください(ガントチャートなどで管理しやすくなります) ›
16
+ ? 「窓口」を選択してください …
17
+ ? 「ディレクション」を選択してください …
18
+ ? 「情報設計」を選択してください …
19
+ ? 「ビジュアルデザイン」を選択してください …
20
+ ? 「フロントエンド」を選択してください …
21
+ ? 「システム」を選択してください …
22
+ ```
23
+
24
+ 逐次、課題が登録されます。
25
+
26
+ ```sh
27
+
28
+ API_TEST-1190 メールフォーム 情報設計・項目定義 @DZ平尾
29
+ API_TEST-1191 メールフォーム 自動返信メール文章作成 @DZ平尾
30
+ API_TEST-1192 デモサイト+開発環境準備 @DZ平尾
31
+ API_TEST-1193 トップページ デザイン @DZ平尾
32
+ API_TEST-1194 トップページ デザイン クライアント確認 @DZ平尾
33
+ API_TEST-1195 トップページ デザイン 戻し修正 @DZ平尾
34
+ API_TEST-1196 下層ページ デザイン @DZ平尾
35
+
36
+
37
+ 🔗 https://xxxxx.backlog.jp/gantt/API_TEST?span=6&scale=days&grouping=3&startDate=2024/01/01
38
+ ```
39
+
40
+ ## `.env`ファイル
41
+
42
+ `.env`ファイルを作成し、以下の内容を記述してください。
43
+
44
+ ```
45
+ BACKLOG_HOST=xxxxx.backlog.jp
46
+ BACKLOG_APIKEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
47
+ NOTION_TOKEN=secret_xxxxxxxxxxxxxx
48
+ ```
@@ -0,0 +1,11 @@
1
+ import type { Role } from './types.js';
2
+ import type { Backlog } from 'backlog-js';
3
+ import type { Project, User } from 'backlog-js/dist/types/entity.js';
4
+ type Params = {
5
+ backlogProject: Project.Project;
6
+ assignedUsers: Record<Role, User.User>;
7
+ backlogCategory?: string;
8
+ log?: (message: string) => void;
9
+ };
10
+ export declare function assign(backlog: Backlog, params: Params): Promise<void>;
11
+ export {};
package/dist/assign.js ADDED
@@ -0,0 +1,67 @@
1
+ import { NotionDB } from '@d-zero/notion';
2
+ import { skipHolydayPeriod } from '@d-zero/shared/skip-holyday-period';
3
+ import dayjs from 'dayjs';
4
+ import { PROJECT_COMMON_TASK_LIST_NOTION_URL } from './define.js';
5
+ export async function assign(backlog, params) {
6
+ if (!process.env.NOTION_TOKEN) {
7
+ throw new Error('NOTION_TOKEN is not defined. Please set it in .env file');
8
+ }
9
+ const project = params.backlogProject;
10
+ const assignedUsers = params.assignedUsers;
11
+ const categoryName = params.backlogCategory?.trim() ?? null;
12
+ const db = new NotionDB(process.env.NOTION_TOKEN, PROJECT_COMMON_TASK_LIST_NOTION_URL);
13
+ const data = await db.getTable({
14
+ sorts: [
15
+ {
16
+ property: '順番',
17
+ direction: 'ascending',
18
+ },
19
+ ],
20
+ });
21
+ if (!data['課題名']) {
22
+ return;
23
+ }
24
+ const categories = await backlog.getCategories(project.id);
25
+ const issueTypes = await backlog.getIssueTypes(project.id);
26
+ const issueTypeTask = issueTypes.find((t) => t.name === 'タスク') ?? issueTypes[0];
27
+ if (!issueTypeTask) {
28
+ throw new Error('issueTypes is not found');
29
+ }
30
+ const category = categoryName
31
+ ? categories.find((c) => c.name === categoryName) ??
32
+ (await backlog.postCategories(project.id, { name: categoryName }))
33
+ : null;
34
+ const now = dayjs();
35
+ let i = 0;
36
+ let currentDate = now.clone();
37
+ let interval = 0;
38
+ for (const value of data['課題名']) {
39
+ const days = +(data['工数(人日)']?.[i] || 1) - 1;
40
+ const { startDate, dueDate } = skipHolydayPeriod(currentDate, currentDate.add(days, 'day'));
41
+ const start = startDate.format('YYYY-MM-DD');
42
+ const due = dueDate.format('YYYY-MM-DD');
43
+ const role = data['担当']?.[i];
44
+ if (!role) {
45
+ throw new Error('role not found');
46
+ }
47
+ const assignedUser = assignedUsers[role];
48
+ const result = await backlog.postIssue({
49
+ projectId: project.id,
50
+ summary: value,
51
+ priorityId: 3,
52
+ issueTypeId: issueTypeTask.id,
53
+ startDate: start,
54
+ dueDate: due,
55
+ assigneeId: assignedUser.id,
56
+ categoryId: category ? [category.id] : [],
57
+ });
58
+ const realDays = dueDate.diff(startDate, 'day');
59
+ params.log?.(`${result.issueKey} ${result.summary} @${assignedUser.name}`);
60
+ interval = +(data['前工程との間隔(人日)']?.[i] || 0) + 1;
61
+ currentDate = currentDate.add(realDays + interval, 'day');
62
+ i++;
63
+ }
64
+ // https://xxx.backlog.jp/gantt/API_TEST?span=6&scale=days&grouping=3&startDate=2024/01/01
65
+ const resultUrl = `${backlog.webAppBaseURL}/gantt/${project.projectKey}?span=6&scale=days&grouping=3&startDate=${now.format('YYYY/MM/DD')}`;
66
+ params.log?.(`🔗 ${resultUrl}`);
67
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env node
2
+ import { Backlog } from 'backlog-js';
3
+ import dotenv from 'dotenv';
4
+ import Enquirer from 'enquirer';
5
+ import minimist from 'minimist';
6
+ import { assign } from './assign.js';
7
+ import { roles } from './define.js';
8
+ import { getBacklogProjectIdFromUrl } from './get-backlog-project-id-from-url.js';
9
+ dotenv.config();
10
+ const cli = minimist(process.argv.slice(2), {
11
+ alias: {
12
+ a: 'assign',
13
+ },
14
+ });
15
+ if (!process.env.BACKLOG_HOST) {
16
+ throw new Error('BACKLOG_HOST is not defined. Please set it in .env file');
17
+ }
18
+ if (!process.env.BACKLOG_APIKEY) {
19
+ throw new Error('BACKLOG_APIKEY is not defined. Please set it in .env file');
20
+ }
21
+ const backlog = new Backlog({
22
+ host: process.env.BACKLOG_HOST,
23
+ apiKey: process.env.BACKLOG_APIKEY,
24
+ });
25
+ if (cli.assign) {
26
+ const users = await backlog.getUsers();
27
+ const dzUsers = users.filter((u) => u.mailAddress.endsWith('@d-zero.co.jp'));
28
+ const { projectId } = await Enquirer.prompt({
29
+ name: 'projectId',
30
+ message: 'BacklogのプロジェクトURLを入力してください',
31
+ type: 'input',
32
+ required: true,
33
+ result(value) {
34
+ return getBacklogProjectIdFromUrl(value);
35
+ },
36
+ });
37
+ const project = await backlog.getProject(projectId);
38
+ const { category } = await Enquirer.prompt({
39
+ name: 'category',
40
+ message: 'カテゴリーを入力してください(ガントチャートなどで管理しやすくなります)',
41
+ type: 'input',
42
+ required: false,
43
+ });
44
+ const assignedUsers = {};
45
+ for (const role of roles) {
46
+ const { userName } = await Enquirer.prompt({
47
+ name: 'userName',
48
+ message: `「${role}」を選択してください`,
49
+ type: 'autocomplete',
50
+ required: true,
51
+ // @ts-ignore
52
+ limit: 5,
53
+ choices: dzUsers.map((u) => ({
54
+ name: u.name,
55
+ message: u.mailAddress,
56
+ hint: u.name,
57
+ value: u.name,
58
+ })),
59
+ });
60
+ const user = dzUsers.find((u) => u.name === userName);
61
+ if (!user) {
62
+ throw new Error(`User ${userName} is not found`);
63
+ }
64
+ assignedUsers[role] = user;
65
+ }
66
+ await assign(backlog, {
67
+ backlogProject: project,
68
+ backlogCategory: category || undefined,
69
+ assignedUsers: assignedUsers,
70
+ log(message) {
71
+ process.stdout.write(message + '\n');
72
+ },
73
+ });
74
+ }
@@ -0,0 +1,3 @@
1
+ import type { Role } from './types.js';
2
+ export declare const PROJECT_COMMON_TASK_LIST_NOTION_URL = "https://www.notion.so/f4cdc643ebcb4221812bce6883e9422c?v=e09c94c1ab14487bb79833c076ba0998";
3
+ export declare const roles: ReadonlySet<Role>;
package/dist/define.js ADDED
@@ -0,0 +1,9 @@
1
+ export const PROJECT_COMMON_TASK_LIST_NOTION_URL = 'https://www.notion.so/f4cdc643ebcb4221812bce6883e9422c?v=e09c94c1ab14487bb79833c076ba0998';
2
+ export const roles = new Set([
3
+ '窓口',
4
+ 'ディレクション',
5
+ '情報設計',
6
+ 'ビジュアルデザイン',
7
+ 'フロントエンド',
8
+ 'システム',
9
+ ]);
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Extracts the project ID from a Backlog URL.
3
+ *
4
+ * Ex:
5
+ * - https://xxx.backlog.jp/projects/API_TEST
6
+ * - https://xxx.backlog.jp/add/API_TEST
7
+ * - https://xxx.backlog.jp/find/API_TEST?projectId=123
8
+ * - https://xxx.backlog.jp/board/API_TEST
9
+ * - https://xxx.backlog.jp/gantt/API_TEST
10
+ * - https://xxx.backlog.jp/wiki/API_TEST/Home
11
+ * - https://xxx.backlog.jp/file/API_TEST
12
+ * - https://xxx.backlog.jp/git/API_TEST
13
+ * - https://xxx.backlog.jp/EditProject.action?project.id=123
14
+ *
15
+ * @param url
16
+ * @returns
17
+ */
18
+ export declare function getBacklogProjectIdFromUrl(url: string): string;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Extracts the project ID from a Backlog URL.
3
+ *
4
+ * Ex:
5
+ * - https://xxx.backlog.jp/projects/API_TEST
6
+ * - https://xxx.backlog.jp/add/API_TEST
7
+ * - https://xxx.backlog.jp/find/API_TEST?projectId=123
8
+ * - https://xxx.backlog.jp/board/API_TEST
9
+ * - https://xxx.backlog.jp/gantt/API_TEST
10
+ * - https://xxx.backlog.jp/wiki/API_TEST/Home
11
+ * - https://xxx.backlog.jp/file/API_TEST
12
+ * - https://xxx.backlog.jp/git/API_TEST
13
+ * - https://xxx.backlog.jp/EditProject.action?project.id=123
14
+ *
15
+ * @param url
16
+ * @returns
17
+ */
18
+ export function getBacklogProjectIdFromUrl(url) {
19
+ try {
20
+ const urlObj = new URL(url);
21
+ const pathname = urlObj.pathname;
22
+ const paths = pathname.split('/').filter((p) => p !== '');
23
+ const projectId = paths[1];
24
+ if (!projectId) {
25
+ const searchParams = urlObj.searchParams;
26
+ const projectId = searchParams.get('project.id');
27
+ if (!projectId) {
28
+ throw new Error(`Project ID not found in URL: ${url}`);
29
+ }
30
+ return projectId;
31
+ }
32
+ return projectId;
33
+ }
34
+ catch (error) {
35
+ if (error instanceof TypeError) {
36
+ return url;
37
+ }
38
+ throw error;
39
+ }
40
+ }
@@ -0,0 +1 @@
1
+ export type Role = '窓口' | 'ディレクション' | '情報設計' | 'ビジュアルデザイン' | 'フロントエンド' | 'システム';
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@d-zero/backlog-projects",
3
+ "version": "0.2.0",
4
+ "description": "A manipulating Backlog projects library",
5
+ "author": "D-ZERO",
6
+ "license": "MIT",
7
+ "private": false,
8
+ "publishConfig": {
9
+ "access": "public"
10
+ },
11
+ "engines": {
12
+ "node": ">=22.1.0"
13
+ },
14
+ "type": "module",
15
+ "bin": {
16
+ "backlog-projects": "./dist/cli.js"
17
+ },
18
+ "files": [
19
+ "dist"
20
+ ],
21
+ "scripts": {
22
+ "build": "tsc",
23
+ "clean": "tsc --build --clean"
24
+ },
25
+ "dependencies": {
26
+ "@d-zero/notion": "1.0.0",
27
+ "@d-zero/shared": "0.2.0",
28
+ "backlog-js": "0.13.1",
29
+ "dayjs": "1.11.11",
30
+ "dotenv": "16.4.5",
31
+ "enquirer": "2.4.1",
32
+ "minimist": "1.2.8"
33
+ },
34
+ "gitHead": "db479312eb17afb95785928ae632814897943ba1"
35
+ }