From 7f13f60c25d28e7d3d5dc806ee403c3d3e9956a8 Mon Sep 17 00:00:00 2001 From: Hiroki Miyaji <40714517+hiroki0525@users.noreply.github.com> Date: Thu, 30 Nov 2023 19:33:11 +0900 Subject: [PATCH] Add notion cli (#75) --- .changeset/lovely-months-obey.md | 5 + packages/cli/README.md | 57 +++++- packages/cli/package.json | 6 +- packages/cli/src/miro/index.ts | 4 +- .../cli/src/notion/__tests__/index.test.ts | 174 ++++++++++++++++++ packages/cli/src/notion/cli.ts | 6 + packages/cli/src/notion/index.ts | 39 ++++ 7 files changed, 281 insertions(+), 10 deletions(-) create mode 100644 .changeset/lovely-months-obey.md create mode 100644 packages/cli/src/notion/__tests__/index.test.ts create mode 100644 packages/cli/src/notion/cli.ts create mode 100644 packages/cli/src/notion/index.ts diff --git a/.changeset/lovely-months-obey.md b/.changeset/lovely-months-obey.md new file mode 100644 index 0000000..b1775ca --- /dev/null +++ b/.changeset/lovely-months-obey.md @@ -0,0 +1,5 @@ +--- +"@dandori/cli": patch +--- + +add notion cli diff --git a/packages/cli/README.md b/packages/cli/README.md index bcb42dc..4cda5ff 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -34,9 +34,14 @@ pnpm --package=@dandori/cli dlx dandori-miro your_tasks.txt -b your_miro_board_i * Please see [@dandori/core](../core/README.md) and [@dandori/ui](../ui/README.md) before using `@dandori/cli`. +## Supported External APIs + +* [Miro](https://miro.com/) +* [Notion](https://www.notion.so/) + ## Commands -### core +### dandori-core This command is to execute `generateDandoriTasks` of `@dandori/core`. @@ -50,23 +55,63 @@ Options: -h, --help display help for command ``` -### miro +#### Example of the command + +```bash +pnpm --package=@dandori/cli dlx dandori-core your_tasks.txt > result.json +``` + +### dandori-miro This command is to execute `generateDandoriMiroCards` of `@dandori/ui`. ```bash % pnpm --package=@dandori/cli dlx dandori-miro -h +Usage: @dandori/cli [options] + Options: -V, --version output the version number -e, --env-file env file path -m, --model Chat GPT model which supports function_calling -o, --optional-task-props optional output task props which delimiter is a comma - -a, --app-card - -b, --board-id + -a, --app-card use app card + -b, --board-id miro board id -h, --help display help for command ``` -## Supported External APIs +#### Example of the command + +```bash +pnpm --package=@dandori/cli dlx dandori-miro your_tasks.txt -b your_miro_board_id +``` + +### dandori-notion -* [Miro](https://miro.com/) \ No newline at end of file +This command is to execute `generateDandoriNotionPages` of `@dandori/ui`. + +```bash +% pnpm --package=@dandori/cli dlx dandori-notion -h + +Usage: @dandori/cli [options] + +Options: + -V, --version output the version number + -e, --env-file env file path + -m, --model Chat GPT model which supports function_calling + -o, --optional-task-props optional output task props which delimiter is a comma + -d, --database-id notion database id + --name notion page name property + --deadline notion page deadline property + --status notion page status property + --status-todo notion page status todo property + --status-doing notion page status doing property + --status-done notion page status done property + -h, --help display help for command +``` + +#### Example of the command + +```bash +pnpm --package=@dandori/cli dlx dandori-miro your_tasks.txt -d your_database_id -o status --status 'Status' --status-todo 'ToDo' --status-doing 'Doing' --status-done 'Done 🙌' +``` \ No newline at end of file diff --git a/packages/cli/package.json b/packages/cli/package.json index 978d7bb..0e6040c 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -21,11 +21,13 @@ "build": "tsup --config ../../tsup.config.ts", "test": "vitest run", "dev:core": "tsx src/core/cli.ts", - "dev:miro": "tsx src/miro/cli.ts tmp/tmp.txt -b uXjVMnCQ-0g=" + "dev:miro": "tsx src/miro/cli.ts", + "dev:notion": "tsx src/notion/cli.ts" }, "bin": { "dandori-core": "./dist/core/cli.js", - "dandori-miro": "./dist/miro/cli.js" + "dandori-miro": "./dist/miro/cli.js", + "dandori-notion": "./dist/notion/cli.js" }, "keywords": [], "author": "Hiroki Miyaji", diff --git a/packages/cli/src/miro/index.ts b/packages/cli/src/miro/index.ts index 2757ec0..45950ce 100644 --- a/packages/cli/src/miro/index.ts +++ b/packages/cli/src/miro/index.ts @@ -14,7 +14,7 @@ export default class DandoriMiroCli extends DandoriCoreCli { protected override buildCommand() { return super .buildCommand() - .option("-a, --app-card") - .option("-b, --board-id "); + .option("-a, --app-card", "use app card") + .option("-b, --board-id ", "miro board id"); } } diff --git a/packages/cli/src/notion/__tests__/index.test.ts b/packages/cli/src/notion/__tests__/index.test.ts new file mode 100644 index 0000000..e718b80 --- /dev/null +++ b/packages/cli/src/notion/__tests__/index.test.ts @@ -0,0 +1,174 @@ +import { + describe, + beforeEach, + afterEach, + vi, + expect, + it, + beforeAll, + afterAll, + Mock, +} from "vitest"; +import { DandoriTask } from "@dandori/core"; +import { generateDandoriNotionPages } from "@dandori/ui"; +import DandoriMiroCli from "../index"; +import { rm, writeFile } from "fs/promises"; + +const tasks: DandoriTask[] = [ + { + id: "1", + name: "task1", + deadline: "2021-01-01", + description: "task1-description", + fromTaskIdList: [], + status: "todo", + }, +]; + +vi.mock("@dandori/core", () => ({ + default: vi.fn(() => tasks), +})); + +vi.mock("@dandori/ui", () => ({ + generateDandoriNotionPages: vi.fn(), +})); + +const mockGenerateDandoriNotionPages = generateDandoriNotionPages as Mock; + +describe("DandoriNotionCli", () => { + const inputFileName = "DandoriNotionCli.txt"; + const inputFileText = "DandoriNotionCli"; + const loadProcessArgv = (options: string[]) => { + process.argv = ["node", "cli.js", inputFileName, ...options]; + }; + + beforeAll(async () => { + await writeFile(inputFileName, inputFileText); + }); + + afterAll(async () => { + await rm(inputFileName); + }); + + afterEach(() => { + process.argv = []; + vi.clearAllMocks(); + }); + + describe("with -d option", () => { + const databaseId = "databaseId"; + + beforeEach(async () => { + loadProcessArgv(["-d", databaseId]); + await new DandoriMiroCli().run(); + }); + + it("call generateDandoriNotionPages with database id", () => { + expect(mockGenerateDandoriNotionPages.mock.lastCall[1]).toContain({ + databaseId, + }); + }); + }); + + describe("with --name option", () => { + const name = "Name"; + + beforeEach(async () => { + loadProcessArgv(["--name", name]); + await new DandoriMiroCli().run(); + }); + + it("call generateDandoriNotionPages with databasePropertiesMap.name", () => { + expect( + mockGenerateDandoriNotionPages.mock.lastCall[1].databasePropertiesMap, + ).toContain({ + name, + }); + }); + }); + + describe("with --deadline option", () => { + const deadline = "Deadline"; + + beforeEach(async () => { + loadProcessArgv(["--deadline", deadline]); + await new DandoriMiroCli().run(); + }); + + it("call generateDandoriNotionPages with databasePropertiesMap.deadline", () => { + expect( + mockGenerateDandoriNotionPages.mock.lastCall[1].databasePropertiesMap, + ).toContain({ + deadline, + }); + }); + }); + + describe("with --status option", () => { + const status = "Status"; + + beforeEach(async () => { + loadProcessArgv(["--status", status]); + await new DandoriMiroCli().run(); + }); + + it("call generateDandoriNotionPages with databasePropertiesMap.status", () => { + expect( + mockGenerateDandoriNotionPages.mock.lastCall[1].databasePropertiesMap, + ).toContain({ + status, + }); + }); + }); + + describe("with --status-todo option", () => { + const statusTodo = "ToDo"; + + beforeEach(async () => { + loadProcessArgv(["--status-todo", statusTodo]); + await new DandoriMiroCli().run(); + }); + + it("call generateDandoriNotionPages with databasePropertiesMap.status.todo", () => { + expect( + mockGenerateDandoriNotionPages.mock.lastCall[1].databasePropertiesMap, + ).toContain({ + "status.todo": statusTodo, + }); + }); + }); + + describe("with --status-doing option", () => { + const statusDoing = "Doing"; + + beforeEach(async () => { + loadProcessArgv(["--status-doing", statusDoing]); + await new DandoriMiroCli().run(); + }); + + it("call generateDandoriNotionPages with databasePropertiesMap.status.doing", () => { + expect( + mockGenerateDandoriNotionPages.mock.lastCall[1].databasePropertiesMap, + ).toContain({ + "status.doing": statusDoing, + }); + }); + }); + + describe("with --status-done option", () => { + const statusDone = "Done"; + + beforeEach(async () => { + loadProcessArgv(["--status-done", statusDone]); + await new DandoriMiroCli().run(); + }); + + it("call generateDandoriNotionPages with databasePropertiesMap.status.done", () => { + expect( + mockGenerateDandoriNotionPages.mock.lastCall[1].databasePropertiesMap, + ).toContain({ + "status.done": statusDone, + }); + }); + }); +}); diff --git a/packages/cli/src/notion/cli.ts b/packages/cli/src/notion/cli.ts new file mode 100644 index 0000000..a74f5b6 --- /dev/null +++ b/packages/cli/src/notion/cli.ts @@ -0,0 +1,6 @@ +#!/usr/bin/env node + +import DandoriNotionCli from "./index"; + +const cli = new DandoriNotionCli(); +await cli.run(); diff --git a/packages/cli/src/notion/index.ts b/packages/cli/src/notion/index.ts new file mode 100644 index 0000000..9e510ac --- /dev/null +++ b/packages/cli/src/notion/index.ts @@ -0,0 +1,39 @@ +import { generateDandoriNotionPages } from "@dandori/ui"; +import DandoriCoreCli from "../core"; + +export default class DandoriNotionCli extends DandoriCoreCli { + override async run(): Promise { + const tasks = await this.generateDandoriTasks(); + const opts = this.program.opts(); + await generateDandoriNotionPages(tasks, { + databaseId: opts.databaseId, + databasePropertiesMap: { + name: opts.name, + deadline: opts.deadline, + description: opts.description, + status: opts.status, + "status.todo": opts.statusTodo, + "status.doing": opts.statusDoing, + "status.done": opts.statusDone, + }, + }); + } + + protected override buildCommand() { + return super + .buildCommand() + .option("-d, --database-id ", "notion database id") + .option("--name ", "notion page name property") + .option("--deadline ", "notion page deadline property") + .option("--status ", "notion page status property") + .option("--status-todo ", "notion page status todo property") + .option( + "--status-doing ", + "notion page status doing property", + ) + .option( + "--status-done ", + "notion page status done property", + ); + } +}