TypeScript対応のDocBase APIクライアント 'node-docbase-sdk'の実装方法

## 概要

[TypeScript対応のDocBase APIクライアントをnpmで公開してみた](https://qiita.com/ffggss/items/e8387d69d39d90e1bf45)の記事でnpmに公開したDocBase APIクライアントで、全てのDocBase APIをTypeScriptで実装できましたので、改めて記事にすることにしました。

所属チーム取得APIが取得できなくなっていたので、関連するメソッドを削除しました(2022年10月23日)
https://help.docbase.io/posts/92977

あと、サンプルコードも公開いたします。

## node-docbase-sdkについて

node-docbase-sdkは、情報共有サービスのDocBase (https://docbase.io/) API用のライブラリです。 TypeScriptに対応しており、TypeScriptで実装したい開発者向けのライブラリです。

https://www.npmjs.com/package/node-docbase-sdk

## API対応表

下記、DocBase API(以下、API)とnode-docbase-sdk（以下、SDK）の対応表です。
2018年1月3日時点で公開されているAPIに対して網羅してみました。

＊`API`で`-`になっているものは、公開APIに存在しません。

[DocBaseAPIの公式マニュアルはこちら](https://help.docbase.io/posts/45703)

| API | SDK | Resource| 操作 | Method | URI | Params |
| --- | --- | --- | --- | --- | --- | --- |
| ○ | ○ | メモ | 取得 | GET | /teams/:domain/posts/:id | - |
| ○ | ○ | メモ | 一覧 | GET | /teams/:domain:/posts | q: string 検索文字列<br>page: number ページ<br>per_page: number 表示数 |
| ○ | ○ | メモ | 作成 | POST | /teams/:domain/posts | - |
| ○ | ○ | メモ | 更新 | PATCH | /teams/:domain/posts/:id | - |
| ○ | ○ | メモ | 削除 | DELETE | /teams/:domain/posts/:id | - |
| - | - | グループ | 取得 | GET | - | - |
| ○ | ○ | グループ | 一覧 | GET | /teams/:domain/groups | 条件検索できない |
| - | - | グループ | 作成 | POST | - | - |
| - | - | グループ | 更新 | PATCH | - | - |
| - | - | グループ | 削除 | DELETE | - | - |
| - | - | タグ | 取得 | GET | - | - |
| ○ | ○ | タグ | 一覧 | GET | /teams/:domain/tags | 条件検索できない |
| - | - | タグ | 作成 | POST | - |  |
| - | - | タグ | 更新 | PATCH | - |  |
| - | - | タグ | 削除 | DELETE | - |  |
| - | - | コメント | 取得 | GET | - |  |
| - | - | コメント | 一覧 | GET | - |  |
| ○ | ○ | コメント | 作成 | POST | /teams/:domain/posts/:id/comments |  |
| - | - | コメント | 更新 | PATCH | - |  |
| ○ | ○ | コメント | 削除 | DELETE | /teams/:domain/comments/:id |  |
| - | - | ファイル | 取得 | GET | - |  |
| - | - | ファイル | 一覧 | GET | - |  |
| ○ | ○ | ファイル | 作成 | POST | /teams/:domain/attachments |  |
| - | - | ファイル | 更新 | PATCH | - |  |
| - | - | ファイル | 削除 | DELETE | - |  |

## 実装方法の解説

### DocBaseオブジェクトの準備

- `DocBase`オブジェクトを使ってをDocBase APIとやりとりします。
- `const docBase: DocBase = new DocBase(DOC_BASE_API_TOKEN, TEAM_NAME)`で`docBase`オブジェクトを用意してください。
- `DOC_BASE_API_TOKEN`には、DocBaseのアクセストークンを設定してください。アクセスートークンの取得方法は、以下、公式マニュアルを参照してください。
  - [アクセストークンについて](https://help.docbase.io/posts/45703#%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%83%88%E3%83%BC%E3%82%AF%E3%83%B3)
- `process.env.DOC_BASE_API_TOKEN`を取得するには、コマンド実行時に環境変数として`DOC_BASE_API_TOKEN`を設定してください。
    - コマンド実行例: ```$ DOC_BASE_API_TOKEN=*** node .```
- `TEAM_NAME`は、domainを指定してください
    - 例えば、`https://hoge.docbase.io`というURLでDocBaseを利用している場合、```const TEAM_NAME='hoge'```です。

```typescript:docBaseオブジェクトの用意
// An access token
const DOC_BASE_API_TOKEN = process.env.DOC_BASE_API_TOKEN;
const TEAM_NAME = 'TEAM_NAME';

const docBase: DocBase = new DocBase(DOC_BASE_API_TOKEN, TEAM_NAME);
```

### Memo conditional search / メモ条件検索

- `q`に検索文字列を入力します。
- 検索オプションについては[こちら](https://help.docbase.io/posts/59432?list=%2Fsearch&q=%E6%A4%9C%E7%B4%A2#%E6%A4%9C%E7%B4%A2%E3%82%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3)を参照してください。

https://help.docbase.io/posts/92984

```typescript:メモ条件検索
  const condition: MemoCondition = <MemoCondition>{};
  condition.q = 'query';
  condition.page = 1;
  condition.perPage = 20;
  const reponse: DocBaseResponse = await docBase.memos.list(condition);
```

### Memo detail / メモ詳細

https://help.docbase.io/posts/97204

```typescript
  const id = 1;
  const reponse: DocBaseResponse = await docBase.memos.find(id);
```

### Memo create / メモ投稿

https://help.docbase.io/posts/92980

```typescript
  const memo: Memo = <Memo>{};
  memo.title = 'title';
  memo.body = 'title';
  memo.draft = false;
  memo.notice = false;
  memo.scope = DisclosureScopes.PRIVATE;
  const reponse: DocBaseResponse = await docBase.memos.create(memo);
```

### Memo update / メモ更新

https://help.docbase.io/posts/92981

```typescript
  const memo: Memo = <Memo>{};
  memo.id = 1;
  memo.title = 'title';
  memo.body = 'body';
  memo.draft = false;
  memo.notice = false;
  memo.scope = DisclosureScopes.PRIVATE;
  const reponse: DocBaseResponse = await docBase.memos.update(memo);
```

### Memo delete / メモ削除

https://help.docbase.io/posts/92982

```typescript
  const id = 1;
  const reponse: DocBaseResponse = await docBase.memos.delete(id);
```

### My team list / 所属チーム取得

https://help.docbase.io/posts/92977

```typescript
  const reponse: DocBaseResponse = await docBase.teams.list();
```

### Comment post / コメント投稿

https://help.docbase.io/posts/216289

```typescript
  const memoId = 1;
  const entity: Comment = <Comment>{};
  entity.memo_id = memoId;
  entity.body = 'COMMENT';
  entity.notice = false;
  const response: DocBaseResponse = await docBase.comments(memoId).create(entity);
```

### Comment delete / コメント削除

https://help.docbase.io/posts/216290

```typescript
  const id = 1;
  const memoId = 1;
  const response: DocBaseResponse = await docBase.comments(memoId).delete(id);
```

### Group list  / グループ取得

https://help.docbase.io/posts/92978

```typescript
  const response: DocBaseResponse = await docBase.groups.list();
```

### Tag list  / タグ取得

https://help.docbase.io/posts/92979

```typescript
  const response: DocBaseResponse = await docBase.tags.list();
```

### File upload  / ファイルアップロード

https://help.docbase.io/posts/225804

```typescript
  const file: File = <File>{};
  file.file_path = '/Users/yfujisawa/Desktop/etc.txt'; 
  const splitPath: string[] = filePath.split('/');
  const fileName = splitPath[splitPath.length - 1];
  file.name = fileName;
  const response: DocBaseResponse = await docBase.files.create(file);
```

## サンプルコード（TypeScript）

```typescript
import { DocBase } from 'node-docbase-sdk/lib/DocBase';
import { DocBaseResponse } from 'node-docbase-sdk/lib/DocBaseResponse';
import { HttpStatus } from 'node-docbase-sdk/lib/enums/HttpStatus';
import { MemoCondition } from 'node-docbase-sdk/lib/conditions/MemoCondition';
import { Memo } from 'node-docbase-sdk/lib/entities/Memo';
import { DisclosureScopes } from 'node-docbase-sdk/lib/enums/DisclosureScopes';
import { Comment } from 'node-docbase-sdk/lib/entities/Comment';
import { Team } from 'node-docbase-sdk/lib/entities/Team';
import { Group } from 'node-docbase-sdk/lib/entities/Group';
import { File } from 'node-docbase-sdk/lib/entities/File';
import { Tag } from 'node-docbase-sdk/lib/entities/Tag';

// Get DocBaseAPI Token from cli.
// ex.
//   $ DOC_BASE_API_TOKEN=<DOC_BASE_API_TOKEN> node .
const DOC_BASE_API_TOKEN = process.env.DOC_BASE_API_TOKEN;
const TEAM_NAME = 'TEAM_NAME';
const KEYWORD = 'DOCBASE_API_TEST';

const docBase: DocBase = new DocBase(DOC_BASE_API_TOKEN, TEAM_NAME);

// コメント削除API
// @see https://help.docbase.io/posts/216290
async function deleteComment(entity: Comment) {
  console.log('== START deleteComment ==');
  const response: DocBaseResponse = await docBase.comments(entity.memo_id).delete(entity.id);
  console.log(`=== response: deleteComment===`);
  console.log(response);
  console.log(`======`);
  if (response.status === HttpStatus.OK) {
    return response.body;
  }
  throw new Error(response.body);
}

// コメント投稿API
// @see https://help.docbase.io/posts/216289
async function postComment(memoId: number) {
  console.log('== START postComment ==');
  const entity: Comment = <Comment>{};
  entity.memo_id = memoId;
  entity.body = 'DOCBASE_API_TEST_COMMENT';
  entity.notice = false;
  const response: DocBaseResponse = await docBase.comments(memoId).create(entity);
  console.log(`=== response: postComment===`);
  console.log(response);
  console.log(`======`);
  if (response.status === HttpStatus.OK) {
    return response.body;
  }
  throw new Error(response.body);
}

// グループ取得API
// @see https://help.docbase.io/posts/92978
async function getGroups() {
  console.log('== START getGroups ==');
  const response: DocBaseResponse = await docBase.groups.list();
  console.log(`=== response: getGroups===`);
  console.log(response);
  console.log(`======`);
  if (response.status === HttpStatus.OK) {
    return response.body;
  }
  throw new Error(response.body);
}

// タグの取得API
// @see https://help.docbase.io/posts/92979
async function getTags() {
  console.log('== START getTags ==');
  const response: DocBaseResponse = await docBase.tags.list();
  console.log(`=== response: getTags===`);
  console.log(response);
  console.log(`======`);
  if (response.status === HttpStatus.OK) {
    return response.body;
  }
  throw new Error(response.body);
}

// ファイルアップロードAPI
// @see https://help.docbase.io/posts/225804
async function uploadFile(filePath: string) {
  console.log('== START uploadFile ==');
  const file: File = <File>{};
  file.file_path = filePath;
  const splitPath: string[] = filePath.split('/');
  const fileName = splitPath[splitPath.length - 1];
  console.log('fileName::' + fileName);
  file.name = fileName;
  const response: DocBaseResponse = await docBase.files.create(file);
  console.log(`=== response: uploadFile===`);
  console.log(response.body);
  console.log(`======`);
  if (response.status === HttpStatus.OK) {
    return response.body;
  }
  throw new Error(response.body);
}

// メモ投稿API
// @see https://help.docbase.io/posts/92980
async function createMemo(): Promise<Memo> {
  console.log('== START createMemo ==');
  const memo: Memo = <Memo>{};
  memo.title = KEYWORD;
  memo.body = KEYWORD;
  memo.draft = false;
  memo.notice = false;
  memo.scope = DisclosureScopes.PRIVATE;
  const response: DocBaseResponse = await docBase.memos.create(memo);
  console.log(`=== response: createMemo===`);
  console.log(response);
  console.log(`======`);
  if (response.status === HttpStatus.OK) {
    return response.body;
  }
  throw new Error(response.body);
}

// メモ更新API
// @see https://help.docbase.io/posts/92981
async function updateMemo(memoId: number): Promise<Memo> {
  console.log('== START updateMemo ==');
  const memo: Memo = <Memo>{};
  memo.id = memoId;
  memo.title = KEYWORD + '_updated';
  memo.body = KEYWORD + '_updated';
  memo.draft = false;
  memo.notice = false;
  memo.scope = DisclosureScopes.PRIVATE;
  const response: DocBaseResponse = await docBase.memos.update(memo);
  console.log(`=== response: updateMemo===`);
  console.log(response);
  console.log(`======`);
  if (response.status === HttpStatus.OK) {
    return response.body;
  }
  throw new Error(response.body);
}

// メモ詳細取得API
// @see https://help.docbase.io/posts/97204
async function findMemo(memoId: number): Promise<Memo> {
  console.log('== START findMemo ==');
  const response: DocBaseResponse = await docBase.memos.find(memoId);
  console.log(`=== response: findMemo ===`);
  console.log(response);
  console.log('======');
  if (response.status === HttpStatus.OK) {
    return response.body;
  }
  throw new Error(response.body);
}

// 複数メモ取得API
// @see https://help.docbase.io/posts/92984
async function searchMemos(keyword: string): Promise<Memo[]> {
  console.log('== START searchMemos ==');
  const condition: MemoCondition = <MemoCondition>{};
  condition.q = keyword;
  condition.page = 1;
  condition.per_page = 20;
  const response: DocBaseResponse = await docBase.memos.list(condition);
  console.log(`=== response: searchMemos===`);
  console.log(response);
  console.log(`======`);
  if (response.status === HttpStatus.OK) {
    return response.body.posts;
  }
  throw new Error(response.body);
}

// メモ削除API
// @see https://help.docbase.io/posts/92982
async function deleteMemo(memoId: number): Promise<boolean> {
  console.log('== START deleteMemo ==');
  const response: DocBaseResponse = await docBase.memos.delete(memoId);
  console.log(`=== response: deleteMemo===`);
  console.log(response);
  console.log(`======`);
  if (response.status === HttpStatus.OK) {
    return true;
  }
  throw new Error(response.body);
}

async function main() {
  try {
    const groups: Group[] = await getGroups();
    const tags: Tag[] = await getTags();
    const createdMemo: Memo = await createMemo();
    const updatedMemo: Memo = await updateMemo(createdMemo.id);
    const memoDetail: Memo = await findMemo(updatedMemo.id);
    const memoList: Memo[] = await searchMemos(memoDetail.title);
    for (const memo of memoList) {
      if (memo.title === 'DOCBASE_API_TEST_updated') {
        console.log(JSON.stringify(memo));
        const comment: Comment = await postComment(memo.id);
        await deleteComment(comment);
        await deleteMemo(memo.id);
      } else {
        throw new Error(JSON.stringify(memo));
      }
    }
  } catch (error) {
    throw error;
  }
}

// == Main ==
main().catch((error) => {
  console.log(error);
});
// ====
```

## サンプルプロジェクト

上記、サンプルコードをすぐに動作確認できるようにGitHubにサンプルプロジェクトも用意しました。ぜひ、ご活用ください。

[サンプルプロジェクトはこちら](https://github.com/YukiFujisawa/node-docbase-sdk-sample)